函数调用的实际实现

 我来答
枫把访274
2016-05-14 · TA获得超过111个赞
知道答主
回答量:181
采纳率:50%
帮助的人:59.2万
展开全部

EBP
EBP是所谓的帧指针,指向当前活动记录的上方(上一个活动记录的最下方)
ESP
ESP是所谓的栈指针,指向当前活动记录的最下方(下一个将要插入的活动记录的最上方)
这两个指针的值规定了当前活动记录的位置 将函数参数压栈:mov eax,dword ptr [n] ;(n为参数变元)
push eax 函数调用将执行如下操作:
⒈将帧指针压入栈中:push ebp
⒉使得帧指针等于栈指针:mov ebp,esp
⒊使栈指针自减,自减得到的内存地址应当能够(足够)用来存储被调用函数的本地状态:sub esp,0CCh
注意:0CCh为0xCC,随着具体函数的不同而不同。 push ebx ;保存ebx寄存器的值
push esi ;保存esi寄存器的值
push edi ;保存edi寄存器的值 lea edi,[ebp-0CCh] ;0cch是当前活动记录的大小。
EDI是目的变址寄存器。 00411417 pop edi
00411418 pop esi
pop ebx 当函数返回时,编译器和硬件将执行如下操作:
⒈使栈指针等于帧指针: mov esp,ebp
⒉从栈中将旧的帧指针弹出: pop ebp
⒊返回:ret ;void function(int n);{push ebp
mov ebp,esp
sub esp,0CCh
push ebx
push esi
push edi
lea edi,[ebp-0CCh]
mov ecx,33h
mov eax,0CCCCCCCCh
rep stos dword ptr es:[edi]
;char a=1;
mov byte ptr [a],1
;if(n==0)return;
cmp dword ptr [n],0
jne function+2Ah (4113CAh)
jmp function+77h (411417h)
;printf(%d\t(0x%08x)\n,n,&n);
mov esi,esp
lea eax,[n]
push eax
mov ecx,dword ptr [n]
push ecx
push offset string %d\t(0x%08x)\n (415750h)
call dword ptr [__imp__printf (4182B8h)]
add esp,0Ch
cmp esi,esp
call @ILT+305(__RTC_CheckEsp) (411136h)
;function(n-1);
mov eax,dword ptr [n]
sub eax,1
push eax
call function (411041h)
add esp,4
;printf(----%d\t(0x%08x)\n,n,&n);
mov esi,esp
lea eax,[n]
push eax
mov ecx,dword ptr [n]
push ecx
push offset string ----%d\t(0x%08x)\n (41573Ch)
call dword ptr [__imp__printf (4182B8h)]
add esp,0Ch
cmp esi,esp
call @ILT+305(__RTC_CheckEsp) (411136h);}
pop edi
pop esi
pop ebx
add esp,0CCh
cmp ebp,esp
call @ILT+305(__RTC_CheckEsp) (411136h)
mov esp,ebp
pop ebp
ret 117: bR = t1(p);
汇编代码如下:
00401FB8 mov ecx,dword ptr [ebp-8] ;将参数放入ecx寄存器
00401FBB push ecx ;参数入栈
00401FBC call @ILT+10(t1) (0040100f) ;函数调用,下一行地址00401FC1入栈
00401FC1 add esp,4 ;函数返回,堆栈指针加4,复原为00401FB8时的值
00401FC4 mov dword ptr [ebp-10h],eax ;从eax中取出高级语言中的函数返回值,放入bR变量中
其中t1函数如下:
125: BOOL t1(void* p)
126: {
00402030 push ebp ;ebp入栈
00402031 mov ebp,esp ;ebp指向此时堆栈的栈顶
00402033 sub esp,44h ;esp减少一个值,空出一段存储区
00402036 push ebx ;将三个寄存器的值入栈,以便在函数中使用它
00402037 push esi ;
00402038 push edi ;
00402039 lea edi,[ebp-44h] ;
0040203C mov ecx,11h ;
00402041 mov eax,0CCCCCCCCh ;
00402046 rep stos dword ptr [edi] ;
127: int* q = (int*)p; ;
00402048 mov eax,dword ptr [ebp+8] ;ebp+8指向函数输入参数的最低位地址;
;如果是ebp+4则指向函数返回地址00401FC1的最低位,值为C1
0040204B mov dword ptr [ebp-4],eax ;
128: return 0;
0040204E xor eax,eax ;返回值放入eax寄存器中
129: }
00402050 pop edi ;三个寄存器出栈
00402051 pop esi ;
00402052 pop ebx ;
00402053 mov esp,ebp ;esp复原
00402055 pop ebp ;ebp出栈,它的值也复原了
00402056 ret ;返回到此时栈顶存储的代码地址:00401FC1
;故而如果不幸被修改了返回地址,程序就会出现意外
以上汇编代码由VC++6.0编译得到。
堆栈在EBP入栈后的情况:
低位 高位
↓ ↓
内存地址 堆栈
┆ ┆
0012F600├────────┤← edi = 0012F600
│ │
0012F604├─┄┄┄ ┄─┤
│ │
│ │
┆ 44h的空间 ┆
┆ ┆
│ │
│ │
0012F640├─┄┄┄┄─┤
│ │
0012F644├────────┤← ebp被赋值后指向该单元,此时ebp=0012F644
│AC F6 12 00 │ebp赋值为esp之前的值
0012F648├────────┤
│C1 1F 40 00 │返回地址
0012F64C├────────┤← ebp + 8
│A0 F6 12 00 │函数实参p的值;
0012F650├────────┤
│ │
├────────┤
┆ ┆
注:存储器存储空间堆栈按从高到低的排列,左边标注的地址是其右下方存储单元的最低位地址。如0012F644指向0012F6AC的AC字节,AC在栈顶。图中存储器中的内容按从低到高位书写,“AC F6 12 00”= 0x0012F6AC
说明
(1)一个c程序由一个或多个程序模块组成,每一个程序模块作为一个源程序文件。对较大的程序,一般不希望把所有内容全放在一个文件中,而是将它们分别放在若干个源文件中,由若干个源程序文件组成一个c程序。这样便于分别编写和编译,调高调试效率。一个源程序文件可以为多个c程序公用。
(2)一个源程序文件由一个或多个函数以及其他有关内容(如指令,数据声明与定义等)组成。一个源程序文件是一个编译单位,子啊程序编译时是以源程序文件为单位进行编译的,而不是以函数为单位进行编译的。
(3)c程序的执行是从main函数开始的,如果在main函数中调用其他函数,在调用后流程返回main函数,在main函数中结束整个程序的进行。
(4)所有函数都是平行的,即在定义函数时是分别进行的,是互相独立的。一个函数并不从属于另一个函数,即函数不能嵌套定义。函数间可以互相调用,但不能调用main函数。main函数是被操作系统调用的。
(5)从用户的角度来看函数分为两种
a:库函数,它是由系统提供的,用户不必自己定义,可直接使用它们。应该说明,不同的c语言编译系统提供的库函数的数量和功能会有一些不同,当然许多基本的函数是共同的。
b:用户自己定义的函数。它是以解决用户专门需求的函数。
(6)从函数的形式来看,函数分为两类。
a:无参函数。无参函数可以带回或不带回函数值,但一般不带回函数值较多。
b:有参函数。在调用函数时,主调函数在调用被调函数时,通过参数向被调函数传递数据。一般情况下,执行调用函数时会得到一个函数值,供主调函数使用。

光点科技
2023-08-15 广告
通常情况下,我们会按照结构模型把系统产生的数据分为三种类型:结构化数据、半结构化数据和非结构化数据。结构化数据,即行数据,是存储在数据库里,可以用二维表结构来逻辑表达实现的数据。最常见的就是数字数据和文本数据,它们可以某种标准格式存在于文件... 点击进入详情页
本回答由光点科技提供
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式