函数调用_函数调用堆栈 - CSDN
函数调用 订阅
计算机编译或运行时,使用某个函数来完成相关命令。对无参函数调用时则无实际参数表。实际参数表中的参数可以是常数、变量或其它构造类型数据及表达式。各实参之间用逗号分隔。 展开全文
计算机编译或运行时,使用某个函数来完成相关命令。对无参函数调用时则无实际参数表。实际参数表中的参数可以是常数、变量或其它构造类型数据及表达式。各实参之间用逗号分隔。
信息
包括内容
函数表达式
外文名
function reference
嵌套调用
C语言中不允许作嵌套的函数定义
一般形式
在程序中通过对函数的调用来
中文名
函数调用
函数调用一般形式
在程序中通过对函数的调用来执行函数体,其过程与其它语言的子程序调用相似。C语言中,函数调用的一般形式为:函数名(实际参数表)对无参函数调用时则无实际参数表。实际参数表中的参数可以是常数、变量或其它构造类型数据及表达式。各实参之间用逗号分隔。 [1] 
收起全文
精华内容
参与话题
  • C语言的函数调用过程

    万次阅读 多人点赞 2018-05-14 16:42:54
    C语言的函数调用过程先上一段代码#include<stdio.h> int Add(int x, int y) { int z = 0; z = x + y; return z; } #include <stdio.h> int main() { int a = 10; int b = 20; int...

    C语言的函数调用过程

    先上一段代码

    #include<stdio.h>
    int Add(int x, int y)
    {
        int z = 0;
        z = x + y;
        return z;
    }
    #include <stdio.h>
    int main()
    {
        int a = 10;
        int b = 20;
        int c = Add(a, b);
        return 0;
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这个函数计算两个数的值。接下来我们通过反汇编,来看看函数被后的过程。

    反汇编代码

    #include <stdio.h>
    int main()
    {
    01151720  push        ebp  
    01151721  mov         ebp,esp  
    01151723  sub         esp,0E4h  
    01151729  push        ebx  
    0115172A  push        esi  
    0115172B  push        edi  
    0115172C  lea         edi,[ebp-0E4h]  
    01151732  mov         ecx,39h  
    01151737  mov         eax,0CCCCCCCCh  
    0115173C  rep stos    dword ptr es:[edi]  
        int a = 10;
    0115173E  mov         dword ptr [a],0Ah  
        int b = 20;
    01151745  mov         dword ptr [b],14h  
        int c = Add(a, b);
    0115174C  mov         eax,dword ptr [b]  
    0115174F  push        eax  
    01151750  mov         ecx,dword ptr [a]  
    01151753  push        ecx  
    01151754  call        _Add (01151104h)  
    01151759  add         esp,8  
    0115175C  mov         dword ptr [c],eax  
        return 0;
    0115175F  xor         eax,eax  
    }
    01151761  pop         edi  
    01151762  pop         esi  
    01151763  pop         ebx  
    01151764  add         esp,0E4h  
    0115176A  cmp         ebp,esp  
    0115176C  call        __RTC_CheckEsp (01151122h)  
    01151771  mov         esp,ebp  
    01151773  pop         ebp  
    }
    //Add()
    #include<stdio.h>
    int Add(int x, int y)
    {
    001016D0  push        ebp  
    001016D1  mov         ebp,esp  
    001016D3  sub         esp,0CCh  
    001016D9  push        ebx  
    001016DA  push        esi  
    001016DB  push        edi  
    001016DC  lea         edi,[ebp-0CCh]  
    001016E2  mov         ecx,33h  
    001016E7  mov         eax,0CCCCCCCCh  
    001016EC  rep stos    dword ptr es:[edi]  
        int z = 0;
    001016EE  mov         dword ptr [z],0  
        z = x + y;
    001016F5  mov         eax,dword ptr [x]  
    001016F8  add         eax,dword ptr [y]  
    001016FB  mov         dword ptr [z],eax  
        return z;
    001016FE  mov         eax,dword ptr [z]  
    }
    00101701  pop         edi  
    00101702  pop         esi  
    00101703  pop         ebx  
    00101704  mov         esp,ebp  
    00101706  pop         ebp  
    00101707  ret  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    以上就是反汇编代码。现在我们一段一段的进行解读,去看看整个程序是怎么执行的。

    main()函数的创建

    01151720  push        ebp  
    01151721  mov         ebp,esp  
    01151723  sub         esp,0E4h  
    01151729  push        ebx  
    0115172A  push        esi  
    0115172B  push        edi  
    0115172C  lea         edi,[ebp-0E4h]  
    01151732  mov         ecx,39h  
    01151737  mov         eax,0CCCCCCCCh  
    0115173C  rep stos    dword ptr es:[edi]  
    //分割线.........................
    int a = 10;
    0115173E  mov         dword ptr [a],0Ah  
        int b = 20;
    01151745  mov         dword ptr [b],14h 
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这里要给读者朋友们简单说一下变量的含义。ebp 和 esp 是两个通用寄存器。它们可以存储 立即数 和 内存地址。esi是源变址寄存器。edi是目的变址寄存器。 
    它们都可用来储存地址。

    在main()函数之前有一个函数CRTStartUp,ebp为这个函数的栈底,esp为这个函数的栈顶。 
    这里写图片描述 
    接下来通过前两句代码mov 赋值移动,它们的状态变成了如下: 
    这里写图片描述 
    第三句话中,sub是减法指令,这句话使得esp向后移动了0E4h个单位。 
    接下来的三句话,是将ebx,esi,edi压入了栈当中。 
    这里写图片描述 
    第七句话,lea 的意思是load effective address,也就是将[ebp - 0E4h]的地址取出来,赋值给了edi。 
    第八句话,将39h赋值移动到ecx。第九句话,将内容赋值移动给eax。 
    第十句话,rep stos 是重复拷贝数据。 
    注意: 
    ecx寄存器是用来保存复制粘贴的次数,ecx是计数器。 
    eax寄存器是用来传送信息的,eax是累加器,通常用来存储数据。 
    rep 是重复上述指令。 
    stos 是将eax的值赋值移动给 es:[edi]这个地址的存储单元。 
    这里写图片描述 
    现在其中的空白部分被cc cc cc cc充满了。这里cc cc就是经常遇到的“烫烫”。 
    分割线 
    接下来的操作很好理解,其目的是将 a和b创建,并且压入栈中。 
    这里写图片描述

    Add()函数的调用过程

        int c = Add(a, b);
    0115174C  mov         eax,dword ptr [b]  
    0115174F  push        eax  
    01151750  mov         ecx,dword ptr [a]  
    01151753  push        ecx  
    01151754  call        _Add (01151104h)  
    01151759  add         esp,8  
    0115175C  mov         dword ptr [c],eax  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    前四句话,它将a和b找了个寄存器存起来,与此同时,以函数参数列表倒着的方式压入栈中。 
    这里写图片描述 
    方便起见,下面的图将只截取上半部分。

    接下来使用了call指令,这个指令是先将 指令 移动到下一句话,然后再操纵call 后面的内容。在call处按F11我们将跳转到Add()函数。 
    这里写图片描述 
    Add()函数

    
    #include<stdio.h>
    int Add(int x, int y)
    {
    001016D0  push        ebp  
    001016D1  mov         ebp,esp  
    001016D3  sub         esp,0CCh  
    001016D9  push        ebx  
    001016DA  push        esi  
    001016DB  push        edi  
    001016DC  lea         edi,[ebp-0CCh]  
    001016E2  mov         ecx,33h  
    001016E7  mov         eax,0CCCCCCCCh  
    001016EC  rep stos    dword ptr es:[edi]  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这一部分和main函数创建过程非常相似。所以用俩图来表示。 
    这里写图片描述 
    这里写图片描述

        int z = 0;
    001016EE  mov         dword ptr [z],0  
        z = x + y;
    001016F5  mov         eax,dword ptr [x]  
    001016F8  add         eax,dword ptr [y]  
    001016FB  mov         dword ptr [z],eax  
        return z;
    001016FE  mov         eax,dword ptr [z]  
    }
    00101701  pop         edi  
    00101702  pop         esi  
    00101703  pop         ebx  
    00101704  mov         esp,ebp  
    00101706  pop         ebp  
    00101707  ret  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这段代码非常有意思,请读者朋友们一定要注意。 
    首先,我们创建了变量 ptr[z]。然后,将ptr[x]的值放在eax里边,将ptr[y]的值与eax相加后再放在eax里,将eax的值赋值移动到 ptr[z]中。最后,我们需要return z。这个地方编译器再次将ptr[z]的值赋值移动到了eax。这么做的原因是:ptr[x]、ptr[y]、ptr[z]将在函数完成后就会销毁,但是寄存器eax不会被销毁,所以在eax的值非常安全。

    括号外的第四句话非常重要。它将销毁该函数的所有数据。 
    这里写图片描述 
    将ebp弹出后,该点只有一个指针esp。接下来执行ret,即 return 返回。返回到我们刚才call下面的地方。

    main()函数的销毁

    01151759  add         esp,8  
    0115175C  mov         dword ptr [c],eax  
        return 0;
    0115175F  xor         eax,eax  
    }
    01151761  pop         edi  
    01151762  pop         esi  
    01151763  pop         ebx  
    01151764  add         esp,0E4h  
    0115176A  cmp         ebp,esp  
    0115176C  call        __RTC_CheckEsp (01151122h)  
    01151771  mov         esp,ebp  
    01151773  pop         ebp  
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    现在我们跳回到了该段的第一句话,现在esp + 8向高地址进发了。其目的就是消去了形参实例化的a,b。第二句话,我们将eax里保存的数据赋值移动给了c。最后,xor异或,自己和自己异或 其值变为0。

    这里写图片描述 
    之后,我们出栈了 edi,esi,ebx。现在esp指向了最开始我们红色那个箭头 ebp - 0E4h。 
    然后,由于add操作,我们继续向高地址进发,到了我们最开始的地方。 
    这里写图片描述 
    这里写图片描述 
    现在,我们到了cmp指令,cmp是比较指令。它的操作是拿第一个数减去第二个数,如果相减为ZF=0说明相等。但是cmp并不会真的减。到此位置main()函数就真的执行完毕,然后esp,ebp。回到了原来的位置。至于后面的函数调用,并不需要深究。至此我们就看到了在汇编上函数如何实现的

    展开全文
  • 函数的定义与调用

    千次阅读 2018-12-10 10:57:12
    函数的定义与调用 1.函数分类 (1)从用户使用的角度看,函数有两种,分别为系统函数和用户自己定义的函数。系统函数又称库函数,是由编译系统提供的,用户不必自己定义这些函数,可以直接...无参函数调用时不必给...

    函数的定义与调用
    1.函数分类
    (1)从用户使用的角度看,函数有两种,分别为系统函数和用户自己定义的函数。系统函数又称库函数,是由编译系统提供的,用户不必自己定义这些函数,可以直接使用它们;用户自己定义的函数是用于解决用户的专门需要。
    注:库函数在使用时程序中必须包含相应的头文件,如,#include《iostream》等。
    (2)从函数的形式看,函数又分为无参函数和有参函数。无参函数调用时不必给出参数,有参函数调用时要给出参数,在主调函数和被调函数之间有数据传递。
    2定义函数
    定义无参函数的一般形式:类型标识符 函数名 (){声明部分 语句}
    例如,
    无参函数

    定义有参函数的一般形式为:类型标识符 函数名(形式参数列表) {声明部分 语句}
    例如,有参函数
    注:c++要求在定义函数时必须制定函数的类型。
    3.函数参数和函数的值
    在定义函数时函数名后面括号中的变量名称为形式参数,简称形参。形参是被调函数中的变量;在主调函数中调用一个函数时,函数名后面括号中的参数(可以是一个表达式)成为实际函数,简称实参,实参是主调函数赋给被调函数的特定值,在调用时实参必须是一个确定的值。
    例,比较两个数并输出大值比较两数
    函数的返回值
    函数的返回值通过return语句获得。函数只能有唯一的返回值。
    函数返回值的类型就是函数的类型。
    return语句可以是一个表达式,函数先计算表达式后再返回值。
    return语句还可以终止函数,并将控制返回到主调函数。
    return 语句之后的代码是不能执行的代码。
    一个函数中可以有一个以上的return语句,执行到哪一
    个return语句,哪一个语句起作用。
    例,函数返回值
    4.函数的调用
    函数调用的一般格式为:函数名 (实参列表)
    按函数在语句中的作用,可分为函数语句,函数表达式和函数参数。
    在一个函数中调用另一个函数需要具备条件:
    (1) 首先被调用的函数必须是已经存在的函数。
    (2) 如果使用库函数,应用#include命令将有关头文件“包含”到文本件中来。
    (3) 如果使用用户自定义的函数,函数调用遵循先定义、后调用的原则。
    注:(1) 被调函数应出现在主调函数之前;
    (2)如果被调用函数出现在主调函数之后,则函数必须在主调函数
    之前进行声明。
    A.函数的嵌套调用
    c++中,所有函数都是平行独立的,无主次、相互包含之分。函数可以嵌套调用,不可以嵌套定义。
    例,嵌套调用
    B.函数的递归调用
    在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。递归调用递归的基本思想就是把一个不能或不好解决的大问题转化成一个或几个小问题,再把这些小问题进一步分解为更小的问题,最小的问题直接解决,即分治法。

    展开全文
  • C&C++函数调用过程

    千次阅读 2019-01-08 23:53:05
    调用函数主要关注三个方面分别是函数名,返回值和参数列表,我接下来将会深入底层讲解调用函数的过程。 调用函数的过程主要有四方面,①函数参数代入,②函数栈帧开辟,③函数返回值,④函数栈帧回退 首先来看一段...

    调用函数主要关注三个方面分别是函数名,返回值和参数列表,我接下来将会深入底层讲解调用函数的过程。

    调用函数的过程主要有四方面,①函数参数代入,②函数栈帧开辟,③函数返回值,④函数栈帧回退

    首先来看一段简单的c文件代码,和它的汇编码,只需简单浏览即可:

    源码:

    int fun1(int a, int b)
    {
    	int c = a + b;
    	return c;
    }
    
    int main()
    {
    	int a = fun1(10, 20);
    
    	return 0;
    }
    

     汇编码:

    main函数

    int main()
    {
    00C31410  push        ebp  
    00C31411  mov         ebp,esp  
    00C31413  sub         esp,0CCh  
    00C31419  push        ebx  
    00C3141A  push        esi  
    00C3141B  push        edi  
    00C3141C  lea         edi,[ebp-0CCh]  
    00C31422  mov         ecx,33h  
    00C31427  mov         eax,0CCCCCCCCh  
    00C3142C  rep stos    dword ptr es:[edi]  
    	int a = fun1(10, 20);
    00C3142E  push        14h  
    00C31430  push        0Ah  
    00C31432  call        fun1 (0C31127h)  
    00C31437  add         esp,8  
    00C3143A  mov         dword ptr [a],eax  
    
    	return 0;
    00C3143D  xor         eax,eax  
    }
    00C3143F  pop         edi  
    00C31440  pop         esi  
    00C31441  pop         ebx  
    00C31442  add         esp,0CCh  
    00C31448  cmp         ebp,esp  
    00C3144A  call        __RTC_CheckEsp (0C3113Bh)  
    00C3144F  mov         esp,ebp  
    00C31451  pop         ebp  
    00C31452  ret  

    fun1函数

    int fun1(int a, int b)
    {
    009813D0  push        ebp  
    009813D1  mov         ebp,esp  
    009813D3  sub         esp,0CCh  
    009813D9  push        ebx  
    009813DA  push        esi  
    009813DB  push        edi  
    009813DC  lea         edi,[ebp-0CCh]  
    009813E2  mov         ecx,33h  
    009813E7  mov         eax,0CCCCCCCCh  
    009813EC  rep stos    dword ptr es:[edi]  
    	int c = a + b;
    009813EE  mov         eax,dword ptr [a]  
    009813F1  add         eax,dword ptr [b]  
    009813F4  mov         dword ptr [c],eax  
    	return c;
    009813F7  mov         eax,dword ptr [c]  
    }
    009813FA  pop         edi  
    009813FB  pop         esi  
    009813FC  pop         ebx  
    009813FD  mov         esp,ebp  
    009813FF  pop         ebp  
    00981400  ret  

    首先知道ebp为栈底寄存器,esp为栈顶寄存器。push为操作,操作方式为在esp中的栈顶存放数据,栈顶上移。

    在查看fun函数参数如何代入之前,我们先看一下main的栈顶和栈底

    (1)函数参数代入

    两次push后,参数就被放在了main函数的栈顶,且入栈为从右往左,效果如下:

    (2)fun函数栈帧开辟

     

     可以看到,call就相当于调用函数,这里重新设置了栈底ebp和栈顶esp,过程如下:

    查看fun函数的开辟栈帧的过程会发现它与main函数的开辟及其类似,其实,main函数的下面也有主函数参数,也就是可以把main函数也看做为一个普通函数。

    (3)函数返回

    利用寄存器带回,将寄存器的值写入接收返回值的常量

    (4)栈帧回退

    将fun的栈顶指向fun的栈底:

    mian函数中:esp移动8位,消除参数

    现在就回到了main函数的栈顶。

    (5)其他问题

    ①因为参数大小不同,8字节及以上的参数采用的是提前在栈顶开辟内存,以保存大字节的参数。

    ②返回8字节及以上的返回值也是采用提前开辟内存的方法。

    ③有三种不同的约定调用方式,分别为__cedel、__stdcall、__fastcall,这三种方式有一些细节的不同,但是思想相同,本文讲的是__cedel方式。

    展开全文
  • 函数调用过程

    千次阅读 多人点赞 2018-01-31 09:31:13
    今天突然看到有人私信我说一直没写函数调用过程(栈帧的形成和销毁过程)这篇博文,赶紧补上。 刚看的栈帧内容时,我很迷惑,我觉得栈帧创建和销毁很麻烦,几句话根本说不完,而且我好像描述不清楚他的过程,所以...

    今天突然看到有人私信我说一直没写函数调用过程(栈帧的形成和销毁过程)这篇博文,赶紧补上。
    刚看的栈帧内容时,我很迷惑,我觉得栈帧创建和销毁很麻烦,几句话根本说不完,而且我好像描述不清楚他的过程,所以在博文里面遇到函数调用我就规避了。现在再写栈帧调用过程,我觉得其实这个过程没有那么困难(不过还是有些抽象,毕竟计算机底层怎么运行我们也不是很明白)。
    栈帧的创建的销毁过程例子代码:

    int Add(int x,int y)
    {
        int sum = 0;
        sum = x + y;
        return sum;
    }
    
    int main ()
    {
        int a = 10;
        int b = 12;
        int ret = 0;
        ret = Add(a,b);
        return 0;
    }
    

    今天主要用汇编代码去讲述这个过程,首先介绍几个寄存器和简单的汇编指令的意思。
    先看几个函数调用过程涉及到的寄存器:
    (1)esp:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
    (2)ebp:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。
    (3)eax 是”累加器”(accumulator), 它是很多加法乘法指令的缺省寄存器。
    (4)ebx 是”基地址”(base)寄存器, 在内存寻址时存放基地址。
    (5)ecx 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。
    (6)edx 则总是被用来放整数除法产生的余数。
    (7)esi/edi分别叫做”源/目标索引寄存器”(source/destination index),因为在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串.
    在32位平台上,ESP每次减少4字节。
    再看几条简单的汇编指令:
    mov :数据传送指令,也是最基本的编程指令,用于将一个数据从源地址传送到目标地址(寄存器间的数据传送本质上也是一样的)
    sub:减法指令
    lea:取偏移地址
    push:实现压入操作的指令是PUSH指令
    pop:实现弹出操作的指令
    call:用于保存当前指令的下一条指令并跳转到目标函数。
    这些指令当然能看懂最好,可以让你很深刻的理解函数调用过程,不能看懂就只能通过我的描述去理解了。
    进行分析之前,先来了解下内存地址空间的分布:
    进程地址空间分布
    栈空间是向低地址增长的,主要是用来保存函数栈帧。 栈空间的大小很有限,仅有区区几MB大小
    汇编代码实现:
    main函数汇编代码:

    int main ()
    {
    011B26E0  push        ebp  
    011B26E1  mov         ebp,esp 
    011B26E3  sub         esp,0E4h 
    011B26E9  push        ebx  
    011B26EA  push        esi  
    011B26EB  push        edi  
    011B26EC  lea         edi,[ebp-0E4h] 
    011B26F2  mov         ecx,39h 
    011B26F7  mov         eax,0CCCCCCCCh 
    011B26FC  rep stos    dword ptr es:[edi] 
        int a = 10;
    011B26FE  mov         dword ptr [a],0Ah 
        int b = 12;
    011B2705  mov         dword ptr [b],0Ch 
        int ret = 0;
    011B270C  mov         dword ptr [ret],0 
        ret = Add(a,b);
    011B2713  mov         eax,dword ptr [b] 
    011B2716  push        eax  
    011B2717  mov         ecx,dword ptr [a] 
    011B271A  push        ecx  
    011B271B  call        @ILT+640(_Add) (11B1285h) 
    011B2720  add         esp,8 
    011B2723  mov         dword ptr [ret],eax 
        return 0;
    011B2726  xor         eax,eax 
    }
    011B2728  pop         edi  
    011B2729  pop         esi  
    011B272A  pop         ebx  
    011B272B  add         esp,0E4h 
    011B2731  cmp         ebp,esp 
    011B2733  call        @ILT+450(__RTC_CheckEsp) (11B11C7h) 
    011B2738  mov         esp,ebp 
    011B273A  pop         ebp  
    011B273B  ret              

    Add函数汇编代码:

    int Add(int x,int y)
    {
    011B26A0  push        ebp  
    011B26A1  mov         ebp,esp 
    011B26A3  sub         esp,0CCh 
    011B26A9  push        ebx  
    011B26AA  push        esi  
    011B26AB  push        edi  
    011B26AC  lea         edi,[ebp-0CCh] 
    011B26B2  mov         ecx,33h 
    011B26B7  mov         eax,0CCCCCCCCh 
    011B26BC  rep stos    dword ptr es:[edi] 
        int sum = 0;
    011B26BE  mov         dword ptr [sum],0 
        sum = x + y;
    011B26C5  mov         eax,dword ptr [x] 
    011B26C8  add         eax,dword ptr [y] 
    011B26CB  mov         dword ptr [sum],eax 
        return sum;
    011B26CE  mov         eax,dword ptr [sum] 
    }
    011B26D1  pop         edi  
    011B26D2  pop         esi  
    011B26D3  pop         ebx  
    011B26D4  mov         esp,ebp 
    011B26D6  pop         ebp  
    011B26D7  ret              

    。 下面图中详细描述了调用过程地址变化(此处所有地址是取自32位windows系统vs编辑器下的调试过程。):
    函数调用过程
    过程描述:
    1、参数拷贝(参数实例化)。
    2、保存当前指令的下一条指令,并跳转到被调函数。
    这些操作均在main函数中进行。

    接下来是调用Add函数并执行的一些操作,包括:
    1、移动ebp、esp形成新的栈帧结构。
    2、压栈(push)形成临时变量并执行相关操作。
    3、return一个值。
    这些操作在Add函数中进行。

    被调函数完成相关操作后需返回到原函数中执行下一条指令,操作如下:
    1、出栈(pop)。
    2、回复main函数的栈帧结构。(pop )
    3、返回main函数
    这些操作也在Add函数中进行。 至此,在main函数中调用Add函数的整个过程已经完成。
    总结起来整个过程就三步:
    1)根据调用的函数名找到函数入口;
    2)在栈中审请调用函数中的参数及函数体内定义的变量的内存空间
    3)函数执行完后,释放函数在栈中的审请的参数和变量的空间,最后返回值(如果有的话)
    如果你学了微机原理,你会想到cpu中断处理过程,是的,函数调用过程和中断处理过程一模一样。

    函数调用约定:
    这里再补充一下各种调用规定的基本内容。
    _stdcall调用约定

    所有参数按照从右到左压入堆栈,由被调用的子程序清理堆栈

    _cdecl调用约定(The C default calling convention,C调用规定)

    参数也是从右到左压入堆栈,但由调用者清理堆栈。

    _fastcall调用约定

    顾名思义,_fastcall的目的主要是为了更快的调用函数。它主要依靠寄存器传递参数,剩下的参数依然按照从右到左的顺序压入堆栈,并由被调用的子程序清理堆栈。

    本篇博文是按调用约定__stdcall 调用函数。

    展开全文
  • c语言中函数调用的过程

    万次阅读 2018-05-11 15:30:01
    要学习C语言中函数调用的过程,必须要知道程序在内存中各个区域的分布。C语言的函数调用的过程主要分布在栈中,所以我们今天主要研究栈。二.几个基本的汇编指令。 从栈顶入栈称为push 从栈底出栈称为pop三.常用...
  • 函数调用堆栈的过程

    万次阅读 多人点赞 2018-05-31 11:46:43
    本篇来分析函数调用的过程:通过下面一个简单的例子来进入话题:#include&lt;stdio.h&gt; int sum(int a,int b) { int tmp=0; tmp=a+b; return tmp; } int main() { int a=10; int b=20; int ret=0; ...
  • 函数调用过程中函数栈详解

    千次阅读 2018-08-24 14:59:57
    当进程被加载到内存时,会被分成很多段 代码段:保存程序文本,指令指针EIP就是指向代码段,可读可执行不可写,如果发生写操作则会提示segmentation fault ... 栈(Stack):存放局部变量,函数参数,当前状...
  • 浅析函数调用过程

    万次阅读 多人点赞 2018-04-19 04:25:54
    函数的调用过程,栈帧的创建和销毁 ...栈帧是编译器用来实现函数调用过程的一种数据结构。C语言中,每个栈帧对应着一个未运行完的函数。 以Add()函数为例研究一下函数的调用过程。 #include&lt;st...
  • 深入理解函数调用跳转地址

    千次阅读 2019-11-20 16:30:47
    Assembly
  • java开发者在定义类中的方法时,不会关心方法的定义相对于调用语句的位置。 但是python中需要注意:函数必须先定义、后调用...4、但是语句对函数调用,必须在函数调用之后,包括直接调用的函数调用的其他函数也必...
  • 函数调用可以作为一个函数的实参

    万次阅读 2011-02-19 18:11:00
    若已定义的函数有返回值,则以下关于该函数调用的叙述中错误的是( D ) A)函数调用可以作为独立的语句存在 B)函数调用可以作为一个函数的实参 C)函数调用可以出现在表达式中 D)函数调用可以作为一个函数的...
  • 调用自己编写的matlab函数

    万次阅读 2017-08-14 16:24:20
    在matlab中调用自定义的函数
  • #标准的先函数定义,后函数调用 def add1(a,b):  return a+b print add1(1,2) #函数2可以调用后面定义的函数3  #事实上,我们在调用函数2的时候,一样是遵循先定义后调用的准则 def add2(a,b):  return add3...
  • Matlab如何调用并使用函数

    万次阅读 2020-10-03 00:09:23
    第一步: 新建m文件,并写函数: function f = fun(x) f = x+1 第二步:保存到work目录下,不然不能用 第三步:在command窗口输入函数名,并...(在这里函数名笔者定义的是fun,则在命令行中调用也应该是fun(x) ...
  • vs 小技巧
  • C++ 普通函数和模板函数调用规则

    万次阅读 2020-07-29 22:31:23
    1.如果函数模板和普通函数都可以实现,优先调用普通函数 2.可以通过空模板参数列表来强制调用函数模板 3.函数模板也可以发生重载 4.如果函数模板可以产生更好的匹配, 优先调用函数模板 */ #include<iostream>...
  • python中调用自己写的函数

    万次阅读 2018-10-31 12:51:33
    在python中,除了调用下载的扩展库之外,还可以自定义函数,方便自己。 把基础模块放在固定文件夹(或相对固定文件夹),使用sys.append(r’自定义的模块路径’) 实例如下: 在E:\pycharm新建hello.py实现基础功能...
  • Source Insight函数调用关系显示设置

    万次阅读 多人点赞 2014-04-24 10:12:58
    当我们需要设置source Insight的项目代码中函数调用关系时,可通过如下的设置来实现: 1、显示函数调用关系窗口  Source Insight工具栏中“View”—>“Relation Window”,选中“Relation Window”则在右下角...
  • 微信小程序调用函数

    万次阅读 2017-11-08 12:13:49
    var that = this that.test() page{ test:function(){ conlose.log(123) } }
  • R语言中函数定义与调用

    万次阅读 2017-01-15 13:12:16
    函数定义格式: name {  expression } 调用的格式: name(arg1,arg2,...) R脚本中调用自定义的函数: 在自己的R脚本中调用自定义的函数时,需要将函数调入内存中,有2种方法: 1.在RGui中,...
1 2 3 4 5 ... 20
收藏数 3,650,497
精华内容 1,460,198
关键字:

函数调用