二级c语言上机题库步骤分别生成什么文件

C语言实现MATLAB&6.5中M文件的方法(转)
摘要:本文着重描述了运用MATLAB命令将M文件翻译为C语言程序,并修改为可直接调用的C语言函数的方法,使用该方法所需要注意的方法局限性和MATLAB版本差异的影响。运用该方法将能够使C语言能直接使用MATLAB当中已经完成的数学计算功能,大大扩充了C语言的数学计算功能和开发效率。
  关键词:MATLAB;M文件;C语言接口
  众所周知,MATLAB是一个功能强大的数学软件,擅长于用矩阵运算完成各种数学功能。但是其程序需要在MATLAB环境下解释执行,效率不高。如果能将它强大的函数库用于C语言,利用C来编译执行,MATLAB将能发挥更大的作用。所以,MATLAB从5.0开始已经提供了与外部C/C++程序的应用程序接口,为利用C语言调用MATLAB的函数提供了可能。但是MATLAB的接口发展很快,到MATLAB
6.5已经提供了对VC 7.0的支持,同时对C的接口相对于5.X版本有了一定的改变。
  在MATLAB当中,我们利用M文件来实现函数,每一个M文件实现一个单独的功能,这一点和C语言当中的函数是相互对应的。所以,如果我们能将MATLAB中的M文件转化为C语言下的一个单个函数,就能实现MATLAB中相应的功能。
  实现方法
  整个过程可分为三个主要部分,用MATLAB将M文件翻译为C语言文件,从生成的C语言文件提取出有用语句,编写数据转换程序实现参数格式转换。整个过程最终将把M文件翻译成C语言当中的一个具有相同功能的函数,供其它的程序调用。
  本文用一个最简单的M文件来示例:
  文件名:messay.m
function c=messay()
c=sqrt(a)+sqrt(b);
  该m文件实现了计算
  1、将M文件编译为C语言文件
  为了将M文件翻译为C语言文件,需要进行一定的设置,这里假设编写C语言的环境为VC6.0,在MATLAB命令提示符下输入mex
-setup和mbuild -setup命令,在相应选项中选择Microsoft Visual C/C++即可。
  在MATLAB命令行中使用mcc命令将messay.m翻译为C代码。
mcc -m messay.m
  其中的参数-m代表mcc命令将把m文件翻译成C语言的代码。
  翻译命令将在messay.m所在的文件夹下生成三个C语言文件:messay.h,messay.c和messay_main.c。其中messay_main.c提供了main()函数;messay.h提供了整个程序的函数声明;messay.c包含了MATLAB生成的功能函数。这三个文件当中,messay.c中包含了我们所需要的数学函数。
  2、提取有用语句
  通过分析,发现由mcc生成的代码内部参数传送方式由MATLAB链接库规定,难以改动,因此需要提取有用的代码,并更改生成代码的参数传递方式。同时从生成代码的注释中可以看出,真正蕴含M文件功能实现的代码段都在Mmessay()函数当中(该函数名的默认构造方式为前缀M加上M文件的文件名),而其它的生成函数仅实现参数传递和标准化接口服务的功能。
  所以提取代码的具体方法是利用messay.c当中生成的static mxArray * Mmessay(int
nargout_)函数,对该函数进行修改,而其他的生成函数都可以忽略不用。原生成的Mmessay()代码如下:
static mxArray * Mmessay(int nargout_) {
 mexLocalFunctionTable save_local_function_table =
mclSetCurrentLocalFunctionTable(&_local_function_table_messay);
 mxArray * c = NULL;
 mxArray * b = NULL;
 mxArray * a = NULL;
 mlfAssign(&a, _mxarray0_);
 mlfAssign(&b, _mxarray1_);
 mlfAssign(&c, mclPlus(mlfSqrt(mclVv(a, "a")), mlfSqrt(mclVv(b,
 mclValidateOutput(c, 1, nargout_, "c", "messay");
 mxDestroyArray(a);
 mxDestroyArray(b);
 mclSetCurrentLocalFunctionTable(save_local_function_table_);
  在生成代码当中,mclSetCurrentLocalFunctionTable和mclSetCurrentLocalFunctionTable函数为两个外部函数,将参数传给外部,与其相关的部分都对C程序使用数学函数没有影响。最终实际有用并执行运算的只有一句:
mlfAssign(&c, mclPlus(mlfSqrt(mclVv(a, "a")),
mlfSqrt(mclVv(b, "b"))));
  实际上,由MATLAB翻译的C语句中,大部分的和实际计算有关的语句和自生成的函数都以mlf开头,所以寻找有用语句的简单方法就是直接寻找mlf为前缀的代码。
  3、参数格式转换
  应当指出,MATLAB所有的计算都是基于一种名为mxArray的数据结构之上的,所有的浮点数、向量或者是矩阵在MATLAB当中都是通过mxArray结构来进行存储和传递的。当然,MATLAB所提供的所有数学函数也都是基于这样一种数据结构进行运算的。所以,要使用MATLAB的生成代码,就必须将C语言当中常用的浮点数和整数转换为mxArray结构。
  本例中利用MATLAB函数mxArray *mlfScalar(double v)和函数double
*mxGetPr(mxArray
*)来实现参数格式转换。函数mlfScalar()将double型变量存入一个新建的mxArray结构中,并返回指针,而函数mxGetPr()将mxArray结构保存的实数的实部取出。至于其它参数转换方法可参看参考文献3中的相关部分。
  最终可以编写这样一个利用了MATLAB数学函数并实现计算的函数:
double Mmessay(double ina, double inb) {
 mxArray *a,*b,*c; //三个用于MATLAB数学函数计算的参数
 double * //计算结果变量
 a=mlfScalar((double)ina); //利用mlfScalar()进行类型转换
 b=mlfScalar((double)inb);
 mlfAssign(&c, mclPlus(mlfSqrt(mclVv(a, "a")), mlfSqrt(mclVv(b,
 outc=mxGetPr(c); //c获得结果的实部,即结果
 mxDestroyArray(a); //释放空间
 mxDestroyArray(b);
 mxDestroyArray(c);
 return *
  到此,整个翻译过程完成,但是还不能直接调用。在这个函数当中运用到了MATLAB的数学库函数mlfSqrt()、mlcPlus()和数据转换函数mlfScalar()、mxGetPr()。由于这些函数是固化在链接库当中的,为了连接执行,必须加入几个库文件和几个静态链接库lib文件。所需要的库文件为mcc命令生成的messay.c文件当中所加入的库文件,一般为libmatlb.h,而需要加入的静态链接库文件如下:
libmat.lib,libmatlb.lib,libmex.lib,libmx.lib
  如果没有以上文件,可以用VC的lib命令将MATLAB相应的def文件转化为lib文件,转化格式为lib
/def:filename.def /machine:ix86 /out:filename.lib。
  方法的局限
  使用本文所用的方法可以将M文件翻译为C语言的函数,但是要受到两个因素的制约。
  1、功能的实现受到MATLAB C函数库的限制
  这种翻译的机制是由MATLAB提供的,mcc命令能直接翻译的函数也仅局限于MATLAB原有的函数。因为这些函数已经被MATLAB6.5编译好,一般以mlf为前缀,存于动态链接库当中并可被C语言直接调用。这些函数在参考文献4中可以查到。而超出了这个范围的函数,并在M文件当中被嵌套使用,在用mcc进行翻译的时候,mcc将在函数名前加上前缀mlf,并进一步翻译该函数。
  但是,这种翻译受到MATLAB参数传递的限制,而不能直接调用,在编译时会出现找不到相应的外部函数的错误。解决办法是手动将所有被翻译的函数进行参数传递方式的调整。如果M文件当中包含的函数被嵌套翻译的层数很深,这样的工作量是巨大而且不可接受的。
同时很多工具箱当中定义的函数也是不能使用这种方法进行翻译的。MATLAB的工具箱更新速度很快,而相应的MATLAB
C的函数库有一定滞后,导致很多最新的工具箱当中的函数是不能被翻译的。
  2、翻译本身存在的限制
  因为这种翻译是遵守C语言要求的,因而对于内存分配要求和C语言不同的函数和一些关于图形显示类型的函数(包括大量的GUI相关函数)也不能被正确的翻译。例如mash.m和step.m这两个较常用的MATLAB函数,由于上述的限制,就不能用本方法进行翻译。
  对于上述的问题,可以利用在C程序当中运用MATLAB引擎的方法动态调用MATLAB的库函数,基本上可以解决上述所有的函数不能被正确翻译和图形显示的问题。但是,运用MATLAB引擎的方法需要利用ActiveX的自动化服务器,在运行的时候程序会在后台执行一个MATLAB的线程而不能完全脱离MATLAB的环境,也就意味着在纯C的环境下是不能运用的,必须要先安装MATLAB并能够在运行时支持多线程工作。具体的方法可以参阅参考文献3。
  MATLAB 5.X和MATLAB 6.5的区别
  对于本方法有以下几点区别需要注意:
  (1)程序当中所需的库文件由5.X版本的matrix.h、mcc.h、matlab.h改为mex.h、libmatlb.h、libmatlbm.h等库文件。
  (2)程序所需要加入的静态链接库文件由5.X需要的libmmfile.lib、libmatlb.lib、libmcc.lib、libmx.lib改为libmat.lib、libmatlb.lib、libmex.lib、libmx.lib四个文件。
  (3)API函数改动很多,虽然数学函数库即mlf前缀的函数少有改动,但是关于变量建立,内存管理和数据类型转换的函数发生改变,即很多原mcc前缀的函数改为用mx为前缀的函数代替,使得很多5.X翻译的C程序代码不能在6.5相应的库下运行通过。
  (4)6.5版本中直接增加了在VC环境下对M文件的支持。在执行mbuild
-setup的配置命令后,MATLAB在VC中提供了MATLAB Project
Wizard,可在VC环境下直接建立MATLAB的工程来翻译M文件。但是这种翻译方法在遇到未定义函数嵌套时将错误的把函数名翻译为变量名,而mcc命令将进一步翻译内部嵌套的函数
Site:/oliver_yin/articles/2833.html
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
另一种方法:使用Matcom来转换
可以参考:
MATCOM是MathWorks公司开发的为MATLAB中的M文件进行高效解释和调试的集成开发环境。MATCOM编译M文件,先将M文件按照与MATCOM的cpp库的对应关系,翻译为cpp源代码,然后用C编译器将cpp文件编译成相应的exe或dll文件。
  用MATCOM方式,生成的代码可读性好,支持图形函数,支持M文件编译过程中的文件嵌套情况,可脱离MATLAB环境;缺陷为待编译的M文件不能涉及MATLAB的内部类。
具体的使用可以参考:
使用matcom matlab与mfc的混编
Matcom是mathTools公司推出的最早的Matlab到C++的编译器。后来,由于Mathtools被Matlab公司收购,Mtacom对Matlab
5.3以后的版本就没有推出新的版本。相比Matlab自带的编译器Compiler,用Matcom转化代码要简单和方便得多;且对于采用C++的工程和研究人员来说Matcom的C++矩阵库(Matrix
Lib)依然有非常大的利用价值。
通过Matcom连接M函数这类脚本语言或者高级算法语言有三种办法:—是将M文件编译成C++代码,然后在C++工程中插入这些代码,最后编译成为独立的可执行程序;二是直接在C++程序中使用Matcom提供的Matrix,方法与一相似,但常用。最后一种是利用MIDEVA直接生成EXE文件,然后在VC中通过shell调用,方法简单,但通用性差,仅适用于VC中调用Matlab实现图形显示的场合。
Mideva软件平台本身的功能相当强大,提供近千个Matlab的基本功能函数,通过必要的设置,就可以直接实现与C++的混合编程,而不必再依赖Matlab;同时,Mediva还提供编译转换功能,能够将Matlab函数或编写的Matlab程序转换为C++形式的DLL,从而实现脱离Matlab环境对Matlab函数和过程的有效调用,这样就有可能实现对Matlab强大的工具箱函数的利用。
  Mideva的缺点是C++与Matlab混合编写的应用软件必须携带必要的DLL,从而增大了软件的体积(约4M),同时也不能对所有的Matlab函数提供支持,例如采用类库进行设计的部分函数。
1. 目前,Matcom的最高版本为4.5。首先安装Matcom 4.5,如下图
安装Matcom前要已求安装VC++6.0。在安装过程中出现选择编译器对话框,选择”是” 如下图:
<img TITLE="" src="/blog7style/images/common/sg_trans.gif" real_src ="/bmiddle/49dd165c"
ALT="C语言实现MATLAB&6.5中M文件的方法(转)" />
出现选择是否安装了Matlab时,如果本机上安装的是Matlab
5.3版本,可以选择“是(Y)”确认,因为Matcom4.5不支持Matlab
6.1以上的版本。其他选项采用默认设置。Matcom可以独立于Matlab运行,但需要外部的C++编译器,指定Matlab的位置是为了让编译文件中需要的一些系统函数找到路径用的。
安装完成后,在“lib”下,可以找到使用MatcomC++矩阵库(Matrix
Lib)的头文件matlib.h和v4501v.lib文件,在Windows操作系统目录的system32目录下,可以找到使用MatcomC++矩阵库的动态链接库文件v4501v.dll。启动界面MIDEVA,如下图。MIDEVA集成开发环境包括命令行窗口、变量列表窗口、命令列表窗口和编译链接信息窗口等几部分,并有详细的帮助文档。
<img TITLE="" src="/blog7style/images/common/sg_trans.gif" real_src ="/bmiddle/49dd&690"
ALT="C语言实现MATLAB&6.5中M文件的方法(转)" />
Matcom命令输入方法与Matlab相同。如果安装在中文版操作系统时,输入命令前加一空格,否则显示为乱码。
1、启动MIDEVA ,File-&New,新建如下test1.m文件:
x=1:0.1:10; y=sin(x); plot(x,y);
将文件保存&&
m文件保存的默认位置为:matcom安装目录C:\matcom45\samples,生成的C++文件保存的默认位置为:matcom安装目录C:\matcom45\samples\Debug,在该目录下将会产生test1.h、test1.cpp、test1.r
、matlib.pch文件。 2. 在VC中用MFC Wizard(exe)创建一个基于对话框名为matcom01的工程。
在面板上添加一个ID为IDC_BUTTON1按扭。
3、将C:\matcom45\lib\下的matlib.h
和v4501v.lib文件和C:\matcom45\debug下的test1.h文件拷贝到工程目录下,然后在VC中将库文件和头文件加入到工程中:工程-&添加工程-&Files,选择刚刚拷贝到matcom01目录下的matlib.h
、v4501v和test1.h文件
4、在ExamleDlg.cpp中加入如下代码: #include "matlib.h“ #include "Test1.h“
CExamleDlg::OnButton1()中分别添加一个初始化类库调用函数”initM(MATCOM_VERSION)”和一个结束类库调用函数”exitM()”
注:test01.cpp中含有要在vc中添加的代码。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
另外参考:
如何把Matlab中的m文件转化成C语言代码
今天应同学的要求,折腾了一下如题所示的转化,过程略显复杂,但最终结果还是可以,即:现在一打开VC6.0直接会显示一个由M文件向Cpp转化的小工具,如下图所示:
&&&&&&&&&&&&&&&&&&&&&&&
现在把折腾的过程总结如下:
1.已经下载程序matcom
v4.5,如果未下载可到地址中下载;
2.机器已经安装Matlab
7.0或以上版本;
3.机器已经安装了软件Microsoft Visual C++
操作步骤:
1.安装matcom v4.5
注意:安装之前,首先在matlab的安装目录下手动建立文件夹:\MATLAB701\bin\toolbox\matlab\general.
2.第一次运行matcom v4.5
运行MATcom4.5自动搜索VC编译器并提示用户是否安装,之后提示是否安装有MATLAB,回答安装后,如果没有建立文件夹\MATLAB701\bin\toolbox\matlab\general,则发生错误。手动建立1.中所说的文件夹,再重新启动MATcom4.5即可。
3.启动MATLAB,
运行以下命令:
cd c:\matcom45& % MATcom的安装路径
diary mpath
matlabpath
4.复制文件
MATcom4.5\bin\usertype.dat文件到Visual C++
6.0\Common\MSDev98\bin目录。
5.Visual C++ 6.0中的操作
&&&&运行Visual
C++,并从菜单中选择Tools-&Customize-&Add-ins and Macro
Files,选择Browse,改变文件类型为Add-in(.dll),选择%MATcom45%\bin\mvcide.dll文件,确定。
补充如果你安装的VC6.0为中文版,则相应的路径为工具\定制\附加项...
6.在Visual C++的开发环境中可以看到一个Visual
MATcom工具条,安装成功。
感谢原作者。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。 原文地址在 http://m.blog.chinaunix.net/uid-7390305-id-2057207.html
这是从别处看到的一篇文章,感觉写的非常深入细致,是作者学习汇编语言的笔记,但是我觉得这篇文章对理解C函数调用非常有帮助,于是参考作者的步骤在自己的linux机器上实现了一下, 并对文章做了一些细小的更改,在此对原作者致谢,如果作者觉得这样有些冒犯的话,请通知我我会立即撤掉。
X86汇编语言学习手记(1)
作者: Badcoffee
2004年10月
版权所有: 转载时请务必以超链接形式标明文章原始出处、作者信息及本声明.
这是作者在学习X86汇编过程中的学习笔记,难免有错误和疏漏之处,欢迎指正。
1. 编译环境
&& OS: Axianux 1.0
&& Compiler: gcc 3..2.3
&& Linker: Solaris Link Editors 5.x
&& Debug Tool: gdb
&& Editor: vi
2. 最简C代码分析
为简化问题,来分析一下最简的c代码生成的汇编代码:
&&& # vi test1.c
&&& int main()
&& && & return 0;
&&&& 编译该程序,产生二进制文件:
&&& # gcc -o start start.c
# file start&&&&
start: ELF 32-bit LSB executable, Intel 80386, version 1(SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), notstripped
&&&& start是一个ELF&#26684;式32位小端(Little Endian)的可执行文件,动态链接并且符号表没有去除。这正是Unix/Linux平台典型的可执行文件&#26684;式。
&&&& 用gdb反汇编可以观察生成的汇编代码:
[wqf@15h166 attack]$ gdb start
GNU gdb Asianux (6.0post-0..1AX)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General PublicLicense, and you are welcome to change it and/or distribute copies of it undercertain conditions.
Type &show copying& to see the conditions.
There is absolutely no warranty for GDB.& Type &show warranty& for details.
This GDB was configured as&i386-asianux-linux-gnu&...(no debugging symbols found)...Using hostlibthread_db library &/lib/tls/libthread_db.so.1&.
(gdb) disassemble main&&&&&&&&& ---&反汇编main函数
Dump of assembler code for function main:
0x &main&#43;0&:&&& push&&%ebp&& ---&ebp寄存器内容压栈,即保存main函数的上级调用函数的栈基地址
0x &main&#43;1&:&&& mov&&&%esp,%ebp &---& esp&#20540;赋给ebp,设置main函数的栈基址
0x &main&#43;3&:&&& sub&&&$0x8,%esp& ---&通过ESP-8来分配8字节堆栈空间
0x &main&#43;6&:&&& and&&&$0xfffffff0,%esp ---&使栈地址16字节对齐
0x &main&#43;9&:&&& mov&&&$0x0,%eax& ---&& 无意义
0x0804831e &main&#43;14&:&& sub&&&%eax,%esp& ---&& 无意义
0x &main&#43;16&:&& mov&&&$0x0,%eax& &---& 设置函数返回&#20540;0
0x &main&#43;21&:&& leave&&&&---&将ebp&#20540;赋给esp,pop先前栈内的上级函数栈的基地址给ebp,恢复原栈基址.
0x &main&#43;22&:&& ret&& ---&main函数返回,回到上级调用.
0x &main&#43;23&:&& nop
End of assembler dump.
注:这里得到的汇编语言语法&#26684;式与Intel的手册有很大不同,Unix/Linux采用AT&T汇编&#26684;式作为汇编语言的语法&#26684;式,如果想了解AT&T汇编可以参考文章 Linux 汇编语言开发指南.
问题一:谁调用了 main函数?
在C语言的层面来看,main函数是一个程序的起始入口点,而实际上,ELF可执行文件的入口点并不是main而是_start。
gdb也可以反汇编_start:
(gdb)disass _start&&&&&&&&&&---&从_start的地址开始反汇编
Dump of assembler code for function _start:
0x &_start&#43;0&:&xor&&& %ebp,%ebp
0x &_start&#43;2&:& pop &&&%esi
0x &_start&#43;3&:&mov&&& %esp,%ecx
0x &_start&#43;5&:&and&&& $0xfffffff0,%esp
0x0804826c &_start&#43;8&:&push&& %eax
0x0804826d &_start&#43;9&:&push&& %esp
0x0804826e &_start&#43;10&: push&&%edx
0x0804826f &_start&#43;11&: push&&$0x8048370
0x &_start&#43;16&: push&&$0x8048328
0x &_start&#43;21&: push&&%ecx
0x0804827a &_start&#43;22&: push&&%esi
0x0804827b &_start&#43;23&: push&&$0x8048310
0x&_start&#43;28&: call&& 0x8048254&__libc_start_main&
---&在这里调用了main函数
0x &_start&#43;33&: hlt
0x &_start&#43;34&: nop
0x &_start&#43;35&: nop
End of assembler dump.&&
问题二:为什么用EAX寄存器保存函数返回&#20540;?
&&&&& 实际上IA32并没有规定用哪个寄存器来保存返回&#20540;。但是,如果反汇编Solaris/Linux的二进制文件,就会发现,都用EAX保存函数返回&#20540;。
&&&&& 这不是偶然现象,是操作系统的ABI(Application Binary Interface)来决定的。
&&&&& Solaris/Linux操作系统的ABI就是Sytem V ABI。
概念三:SFP (Stack Frame Pointer) 栈帧指针
&&& && 正确理解SFP必须了解:
&&&&&& IA32 的栈的概念
&&&&&& CPU 中32位寄存器ESP/EBP的作用
&&&&&& PUSH/POP 指令是如何影响栈的
&&&&&& CALL/RET/LEAVE 等指令是如何影响栈的
&&&&& 如我们所知:
1)&&&IA32的栈是用来存放临时数据,而且是LIFO,即后进先出的。栈的增长方向是从高地址向低地址增长,按字节为单位编址。
& &&&&2) EBP是栈基址的指针,永远指向栈底(高地址),ESP是栈指针,永远指向栈顶(低地址)。
&&&&& 3) PUSH一个long型数据时,以字节为单位将数据压入栈,从高到低按字节依次将数据存入ESP-1、ESP-2、ESP-3、ESP-4的地址单元。
&&&&& 4) POP一个long型数据,过程与PUSH相反,依次将ESP-4、ESP-3、ESP-2、ESP-1从栈内弹出,放入一个32位寄存器。
&&&&& 5) CALL指令用来调用一个函数或过程,此时,下一条指令地址会被压入堆栈,以备返回时能恢复执行下条指令。
&&&&& 6) RET指令用来从一个函数或过程返回,之前CALL保存的下条指令地址会从栈内弹出到EIP寄存器中,程序转到CALL之前下条指令处执行。
&&&&& 7) ENTER是建立当前函数的栈框架,即相当于以下两条指令:
&&&&&&&&& pushl&&%ebp
&&&&&&&&&movl&&& %esp,%ebp
&&&&& 8) LEAVE是释放当前函数或者过程的栈框架,即相当于以下两条指令:
&&&&&&&&&movl ebp, esp
&&&&&&&&&popl& ebp
原来编译器会自动在函数入口和出口处插入创建和释放栈框架的语句。
&&&&&&& 函数被调用时:
&&&&&&& 1) EIP/EBP成为新函数栈的边界
&&&&&&&&&&&函数被调用时,返回时的EIP首先被压入堆栈;创建栈框架时,上级函数栈的EBP被压入堆栈,与EIP一道行成新函数栈框架的边界。& &&&&&&
&&&&&&& 2) EBP成为栈帧指针STP,用来指示新函数栈的边界
&&&&&&&&&&& 栈帧建立后,EBP指向的栈的内容就是上一级函数栈的EBP,可以想象,通过EBP就可以把层层调用函数的栈都回朔遍历一遍,调试器就是利用这个特性实现backtrace功能的。
& & & & 3) ESP总是作为栈指针指向栈顶,用来分配栈空间
& & &&&&&&& 栈分配空间给函数局部变量时的语句通常就是给ESP减去一个常数&#20540;,例如,分配一个整型数据就是 ESP-4。
&&&&&&& 4) 函数的参数传递和局部变量访问可以通过STP即EBP来实现
&&&&&&&&&&& 由于栈框架指针永远指向当前函数的栈基地址,参数和局部变量访问通常为如下形式:
&&& && && &&&#43;8&#43;xx(%ebp)&& && && :函数入口参数的的访问
&&&& &&&&&& -xx(%ebp)&&& &&&&&& & :函数局部变量访问
假如函数A调用函数B,函数B调用函数C ,则函数栈帧及调用关系如下图所示:
&&&&&& &#43;----------------------&#43;----& 高地址
&&&&&& | EIP (上级函数返回地址)& |&&
&&&&&& &#43;----------------------&#43;&&
& &#43;--& | EBP (上级函数的EBP)&&& | --&#43;&&&& &------ 当前函数A的EBP (即STP框架指针)
& |&&& &#43;----------------------&#43;&& &#43;--&偏移量A&&&
& |&&& | Local Variables&&&&& |&& |
& |&&& |&&& ..........&&&&&&& | --&#43;&&&& &------ ESP指向函数A新分配的局部变量,局部变量可以通过A的ebp-偏移量A访问&&
& | f& &#43;----------------------&#43;
& | r& | Arg n(函数B的第n个参数) |
& | a& &#43;----------------------&#43;
& | m& | Arg .(函数B的第.个参数) |
& | e& &#43;----------------------&#43;
& |&&& | Arg 1(函数B的第1个参数) |
& | o& &#43;----------------------&#43;
& | f& | Arg 0(函数B的第0个参数) |&& --&#43;&& &------ B函数的参数可以由B的ebp&#43;偏移量B访问
& |&&& &#43;----------------------&#43;&&&& &#43;--& 偏移量B
& | A& | EIP (A函数的返回地址)&& |&&&& |
& |&&& &#43;----------------------&#43;&& --&#43;
& &#43;--- | EBP (A函数的EBP)&&&&& |&--&#43;& &------ 当前函数B的EBP (即STP框架指针)
&&&&&& &#43;----------------------&#43;&& |
&&&&&& | Local Variables&&&&& |&& |
&&&&&& |&&& ..........&&&&&&& |&& |& &------ ESP指向函数B新分配的局部变量
&&&&&& &#43;----------------------&#43;&& |
&&&&&& | Arg n(函数C的第n个参数) |&& |
&&&&&& &#43;----------------------&#43;&& |
&&&&&& | Arg .(函数C的第.个参数) |&& |
&&&&&& &#43;----------------------&#43;&& &#43;--& frame of B
&&&&&& | Arg 1(函数C的第1个参数) |&& |
&&&&&& &#43;----------------------&#43;&& |
&&&&&& | Arg 0(函数C的第0个参数) |&& |
&&&&&& &#43;----------------------&#43;&& |
&&&&&& | EIP (B函数的返回地址)&& |&& |
&&&&&& &#43;----------------------&#43;&& |
&&#43;--&& | EBP (B函数的EBP)&&&&& |---&#43;& &------ 当前函数C的EBP (即STP框架指针)
&|&&&& &#43;----------------------&#43;
&|&&&& | Local Variables&&&&& |
&|&&&& |&&& ..........&&&&&&& |&&&&& &------ ESP指向函数C新分配的局部变量
&|&&&& &#43;----------------------&#43;----& 低地址
frame of C
概念四:Stack aligned 栈对齐
&&&&&&那么,以下语句到底是和作用呢?
&&&&&&subl&&& $8,%esp
&&&&&&andl&&& $0xfffffff0,%esp&&&& ---&通过andl使低4位为0,保证栈地址16字节对齐
表面来看,这条语句最直接的后果是使ESP的地址后4位为0,即16字节对齐,那么为什么这么做呢?
原来,IA32 系列CPU的一些指令分别在4、8、16字节对齐时会有更加的运行速度,因此gcc编译器为提高生成代码在IA32上的运行速度,默认对产生的代码进行16字节对齐.
andl&&& $0xf0,%esp 的意义很明显,那么 subl&&&$8,%esp 呢,是必须的吗?这里假设在进入main函数之前,栈是16字节对齐的,那么,进入main函数后,EIP被压入堆栈后,栈地址最末4位必定是0100,esp-8则恰好使后4位地址为0。看来,这也是为保证栈16字节对齐的。
如果查一下gcc的手册,就会发现关于栈对齐的参数设置:
&&&&&& -mpreferred-stack-boundary=n&&---& 希望栈按照2的n次的字节边界对齐, n的取&#20540;范围是2-12.
默认情况下,n是等于4的,也就是说,默认情况下,gcc是16字节对齐,以适应IA32大多数指令的要求。
让我们利用-mpreferred-stack-boundary=2来去除栈对齐指令:
(gdb) disass main
Dump of assembler code for function main:
0x &main&#43;0&:&&&push&& %ebp
0x &main&#43;1&:&&&mov&&& %esp,%ebp
0x &main&#43;3&:&&&mov&&& $0x0,%eax
0x &main&#43;8&:& &&leave
0x &main&#43;9&:&&& ret
0x0804831a &main&#43;10&:&& nop
0x0804831b &main&#43;11&:&& nop
End of assembler dump.
可以看到,栈对齐指令没有了,因为,IA32的栈本身就是4字节对齐的,不需要用额外指令进行对齐。
问题五:栈框架指针STP是不是必须的呢?
[wqf@15h166attack]$ gcc -mpreferred-stack-boundary=2-fomit-frame-pointer start.c -o start
[wqf@15h166attack]$ gdb& start
(gdb) disass main
Dump of assembler code forfunction main:
0x &main&#43;0&:&&& mov&&&$0x0,%eax
0x &main&#43;5&:&&& ret
0x &main&#43;6&:&&& nop
0x &main&#43;7&:&&& nop
End of assembler dump.
由此可知,-fomit-frame-pointer 可以去除STP。
去除STP后有什么缺点呢?
&&&&&&1)增加调式难度
&&& &&& & 由于STP在调试器backtrace的指令中被使用到,因此没有STP该调试指令就无法使用。
&&& && 2)降低汇编代码可读性
&&& &&& & 函数参数和局部变量的访问,在没有ebp的情况下,都只能通过&#43;xx(esp)的方式访问,而很难区分两种方式,降低了程序的可读性。
去除STP有什么优点呢?
&&&&& &1)节省栈空间。
&&&&&&2)减少建立和撤销栈框架的指令后,简化了代码。
&&&&&&3)使ebp空闲出来,使之作为通用寄存器使用,增加通用寄存器的数量
&&&& & 4)以上3点使得程序运行速度更快。
概念六:Calling Convention&调用约定和ABI (Application Binary Interface) 应用程序二进制接口。
&&&&&&&函数如何找到它的参数?
&&&&&& 函数如何返回结果?
&&&&& &函数在哪里存放局部变量?
&&&&& &&哪一个硬件寄存器是起始空间?
&&&&& &&哪一个硬件寄存器必须预先保留?
Calling Convention&调用约定对以上问题作出了规定。CallingConvention也是ABI的一部分。因此,遵守相同ABI规范的操作系统,使其相互间实现二进制代码的互操作成为了可能。
例如:由于Solaris、Linux都遵守System V的ABI,Solaris10就提供了直接运行Linux二进制程序的功能。
本文通过最简的C程序,引出以下概念:
STP 栈框架指针
Stack aligned 栈对齐
Calling Convention& 调用约定 和 ABI (Application Binary Interface) 应用程序二进制接口
今后,将通过进一步的实验,来深入了解这些概念。通过掌握这些概念,使在汇编级调试程序产生的core dump、掌握C语言高级调试技巧成为了可能。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:8295次
排名:千里之外
(5)(2)(2)(1)

我要回帖

更多关于 c语言上机操作 的文章

 

随机推荐