精华内容
下载资源
问答
  • 函数调用过程中函数栈详解

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

    当进程被加载到内存时,会被分成很多段

    1. 代码段:保存程序文本,指令指针EIP就是指向代码段,可读可执行不可写,如果发生写操作则会提示segmentation fault
    2. 数据段:保存初始化的全局变量和静态变量,可读可写不可执行
    3. BSS:未初始化的全局变量和静态变量
    4. 堆(Heap):动态分配内存,向地址增大的方向增长,可读可写可执行
    5. 栈(Stack):存放局部变量,函数参数,当前状态,函数调用信息等,向地址减小的方向增长,可读可写可执行
    6. 环境/参数段(environment/argumentssection):用来存储系统环境变量的一份复制文件,进程在运行时可能需要。例如,运行中的进程,可以通过环境变量来访问路径、shell 名称、主机名等信息。该节是可写的,因此在缓冲区溢出(buffer overflow)攻击中都可以使用该段

    寄存器

    EAX:累加(Accumulator)寄存器,常用于函数返回值

    EBX:基址(Base)寄存器,以它为基址访问内存

    ECX:计数器(Counter)寄存器,常用作字符串和循环操作中的计数器

    EDX:数据(Data)寄存器,常用于乘除法和I/O指针

    ESI:源变址寄存器

    DSI:目的变址寄存器

    ESP:堆栈(Stack)指针寄存器,指向堆栈顶部

    EBP:基址指针寄存器,指向当前堆栈底部

    EIP:指令寄存器,指向下一条指令的地址

    入栈push和出栈pop

    push ebp就等于将ebp的值保存到栈中,并且将当前esp下移

    pop ebp就等于将ebp的值从栈中取出来,将ebp指向这个值

    下面用一个例子来讲函数调用过程中栈的变化

    int sum(int _a,int _b)
    {
        int c=0;
        c=_a+_b;
    
        return c;
    }
    
    
    int main()
    {
        int a=10;
        int b=20;
    
        ret=sum(a,b);
    
        return 0;
    }

    main函数的栈在调用之前如图:

    Ok现在讲一讲ret=sum(a,b);的执行过程

    Step 1

    函数参数从右至左入栈

    Step 2

    ret=sum(a,b);

    call @ILT+0(sum) (00401005) call指令实际上分两步

               push EIP 将下一条指令入栈保存起来

               esp-4 esp指针下移

    Step 3:

    push ebp 将main函数基指针入栈保存

    mov ebp esp 将esp的值存入ebp也就等于将ebp指向esp

    sub esp 44H将esp下移动一段空间创建sum函数的栈栈帧

    Step 4

    push ebx

    push esi

    push edi

    lea edi,[ebp-44h] 从ebp-44h的地方开始拷贝

    mov ecx,11h 拷贝11次

    mov eax,0CCCCCCCCh 拷贝内容为0CCCCCCCCh

    rep stos dword ptr [edi] 每次拷贝双字

    Step 5

    int c = 0

    mov dword ptr [ebp-4],0 将sum的局部变量c放入[ebp-4]的空间内

    Step 6

    执行函数操作

    Step 7

    return c = 0

    mov eax,dword ptr [ebp-4] 将返回值(变量c所在的地址的内容)放入eax寄存器保存住

    Step 8

    pop edi //将之前入栈的值重新返回给edi寄存器

    0040104C pop exi 将之前入栈的值重新返回给exi寄存器

    0040104D pop ebx 将之前入栈的值重新返回给ebx寄存器

    Step 9

    mov esp ebp //将ebp的值赋给esp,也就等于将esp指向ebp,销毁sum函数栈帧

    Step 10

    pop ebp //ebp出栈,将栈中保存的main函数的基址赋值给ebp

    Step 11

    ret //ret相当于pop eip 就是把之前保存的函数返回地址(也就是main函数中下一条该执行的指令的地址)出栈

    Step 12

    add esp,8 //此时若传入sum函数的参数已经不需要了,我们将esp指针上移

    此时函数整个调用过程就结束了,main函数栈恢复到了调用之前的状态

    展开全文
  • 函数调用过程

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

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

    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 调用函数。

    展开全文
  • 原创作品:陈晓爽(cxsmarkchan) 转载请注明出处 《Linux操作系统分析》MOOC课程 学习笔记 本文通过汇编一段含有简单函数调用的C程序,说明在函数调用过程中堆栈的变化。

    原创作品:陈晓爽(cxsmarkchan)
    转载请注明出处
    《Linux操作系统分析》MOOC课程 学习笔记

    本文通过汇编一段含有简单函数调用的C程序,说明在函数调用过程中堆栈的变化。

    1 C程序及其汇编代码

    1.1 C程序源码

    本文使用的C程序源码如下:

    //main.c
    int g(int x){
        return x + 5;
    }
    
    int f(int x){
        return g(x);
    }
    
    int main(void){
        return f(9) + 3;
    }

    这个程序仅含有main函数和两个函数调用,不含输入输出。不难看出程序的运行过程为:
    - main函数调用f函数,传入参数9;
    - f函数调用g函数,传入参数9;
    - g函数返回9+5,即14;
    - f函数返回14;
    - main函数返回14+3,即17.

    1.2 汇编代码

    在linux(本文平台为实验楼Linux内核分析)下执行如下编译指令:

    gcc -S -o main.s main.c -m32

    该指令可将main.c编译为汇编代码(而非二进制执行文件),输出到main.s。参数“-m32”表示采用32位的方式编译。

    用vim打开main.s文件,可以看到如下图的汇编代码:

    原始汇编代码

    在main.s中,有大量的以“.”开头的行,这些行只是用于链接的说明性代码,在实际的程序中并不会出现。为便于分析,可以将这些内容删去,得到纯粹的汇编代码,如下:

    g:
    02  pushl %ebp
    03  movl %esp, %ebp
    04  movl 8(%ebp), %eax
    05  addl $5, %eax
    06  popl %ebp
    07  ret
    f:
    09  pushl %ebp
    10  movl %esp, %ebp
    11  subl $4, %esp
    12  movl 8(%ebp), %eax
    13  movl %eax, (%esp)
    14  call g
    15  leave
    16  ret
    main:
    18  pushl %ebp
    19  movl %esp, %ebp
    20  subl $4, %esp
    21  movl $9, (%esp)
    22  call f
    23  addl $3, %eax
    24  leave
    25  ret

    汇编代码中的pushl/popl/movl/subl/addl操作,末尾的字母“l”代表32位操作。
    可以看出,汇编代码一共分为3部分,即3个函数。在main函数中调用了call f指令,在f函数中调用了call g指令,和C程序的逻辑是一致的。但是,不同于C程序的简洁明了,汇编代码在调用call指令之前,还有一系列的赋值(movl)、压栈(pushl)、弹栈(popl)等操作,且其处理对象均为ebp、esp等寄存器。这些操作的含义是本文的重点。

    2 函数调用过程中的堆栈分析

    2.1 相关的寄存器

    本文涉及的寄存器变量如下:

    寄存器变量 含义
    eax 返回值寄存器,用于存储函数返回值
    eip 当前指令地址寄存器,其值为内存中的指令地址,只能通过jmp, call, ret等修改,不可直接修改
    ebp 堆栈底端地址寄存器,其值对应内存中的堆栈底端
    esp 堆栈顶端地址寄存器,其值对应内存中的堆栈顶端,ebp-esp之间即堆栈内容

    2.2 push和pop指令

    故名思议,push和pop操作对应堆栈的压栈和弹栈操作。在push和pop操作时,ebp即堆栈底端的位置不变,而esp即堆栈顶端的位置会改变,操作均在esp处进行。值得注意的是,注意到堆栈在内存中是降序排列,即ebp的值大于esp的值。每进行一次压栈操作,esp均会减小4;每进行一次弹栈操作,esp均会增加4。
    因此,pushl %A等价于:

    subl $4, %esp ;堆栈顶端指针向下移动4字节(即32位)
    movl %A, (%esp) ;将寄存器A的内容存到寄存器esp对应的内存地址

    类似地,popl %A等价于:

    movl (%esp), %A ;将堆栈顶端对应的内存中的内容幅值给A
    addl $4, %esp ;堆栈顶端上移4字节

    2.3 函数调用:call指令和ret指令

    2.3.1 call和ret对应的堆栈操作

    call f 等价于如下汇编代码:

    pushl %eip ;将当前的程序位置压栈
    movl f, %eip ;将f函数的入口位置赋给eip,则下一条指令执行f的入口指令。

    ret 等价于如下汇编代码:

    popl %eip 

    由此可见,函数调用的时候,首先是在堆栈中存入当前的程序位置,再跳转到被调用的函数入口。在返回时,从堆栈中弹出当前的程序位置即可。
    在call语句调用结束后,访问eax寄存器,即可得到函数返回值。
    值得注意的是,eip指令是不允许直接被调用的,因此上述等价程序并不能直接写在汇编代码中,只能通过call和ret来间接执行。

    2.3.2 参数的传入

    很容易发现这样的问题:在调用函数f(9)时,调用call即意味着跳转,那么9作为函数参数,应该如何传递给被调用的函数?
    答案是:参数的传入也通过堆栈来进行。
    在汇编代码中,注意call f指令之前有如下语句:

    subl $4, %esp
    movl $9, (%esp)

    这两条语句实际上等价于pushl $9,因此,在调用call语句之前,9这个参数就已经被压入堆栈中。进入函数f时,堆栈顶端为前一个eip,堆栈第2项即为参数9。因此,只需访问4(%esp)即可获取相应的参数。
    当然, 我们从实际代码中还会发现如下问题:
    1. 在函数f中,并未调用4(%esp),而是调用了8(%ebp)获取参数9。
    2. 在对9进行压栈后,并未进行弹栈操作。
    其原因在于被调用函数中会建立子堆栈,详见2.4节。

    2.4 被调用函数的堆栈关系:enter和leave指令

    在本文的汇编代码中并没有enter指令,但enter指令等价于如下汇编指令:

    pushl %ebp
    movl %esp, %ebp

    这实际上是每个函数入口的两条语句。
    而leave指令则等价于:

    movl %ebp, %esp
    popl %ebp

    注意到函数g中并没有leave语句,只有popl %ebp语句。这是因为函数g中没有修改esp,因此只需将ebp弹栈。
    enter语句在函数入口处,将ebp压栈,并将esp赋给ebp。这样,ebp指针被下移到esp处,相当于构建了一个新的空堆栈。应该注意,原堆栈的内容并没有被覆盖,而是在ebp的上方。此时构建的新堆栈即为函数的子堆栈,函数通过ebp区分函数内部的变量和外部的变量。
    leave语句在函数结束处,在ret语句前。它首先将ebp的值赋给esp,而ebp的值正是在enter语句中,由原先的esp赋给ebp的。因此,movl %ebp, %esp语句可以将esp恢复到函数调用前,call语句调用后的状态。接下来,调用pop语句将ebp恢复到函数调用前的状态,即可调用ret语句返回。
    这样,就可以回答上一节中的两个问题:
    1. 在enter之后,形成了一个新的堆栈,ebp成为原函数堆栈和被调用函数堆栈的分界点。ebp的正前方4(%ebp)是前一个函数的运行位置指针,再往前8(%ebp)即为函数参数的位置。
    2. 由于leave语句的存在,函数堆栈可以直接恢复到函数调用前的状态,不需要依次进行弹栈。

    3 汇编程序运行过程

    综上,我们可以得到汇编语言的整体运行过程,和运行过程中的堆栈变化。假设ebp和esp的初始值为100,运行过程按顺序如下(null表示堆栈空间已开辟,但尚未存入数值):

    eip 所在函数 语句 eax ebp esp 堆栈内容(括号内是ebp前方的内容) 下一个eip
    18 main pushl %ebp 100 96 100 19
    19 main movl %esp, %ebp 96 96 100 20
    20 main subl $4, %esp 96 92 100,null 21
    21 main movl $9, (%esp) 96 92 100,9 22
    22 main call f 96 88 100,9,22 09
    09 f pushl %ebp 96 84 100,9,22,96 10
    10 f movl %esp, %ebp 84 84 (100,9,22),96 11
    11 f subl $4, %esp 84 80 (100,9,22),96,null 12
    12 f movl 8(%ebp), %eax 9 84 80 (100,9,22),96,null 13
    13 f movl %eax, (%esp) 9 84 80 (100,9,22),96,9 14
    14 f call g 9 84 76 (100,9,22),96,9,14 02
    02 g pushl %ebp 9 84 72 (100,9,22),96,9,14,84 03
    03 g movl %esp, %ebp 9 72 72 (100,9,22,96,9,14),84 04
    04 g movl 8(%ebp), %eax 9 72 72 (100,9,22,96,9,14),84 05
    05 g addl $5, %eax 14 72 72 (100,9,22,96,9,14),84 06
    06 g popl %ebp 14 84 76 (100,9,22),96,9,14 07
    07 g ret 14 84 80 (100,9,22),96,9 15
    15 f leave 14 96 84 100,9,22 16
    16 f ret 14 96 88 100,9 23
    23 main addl $3, %eax 17 96 88 100,9 24
    24 main leave 17 100 100 25
    25 main ret 17



    其中,leave语句的堆栈变化由两步组成,以15行的语句为例,实际应拆开为:

    eip 所在函数 语句 eax ebp esp 堆栈内容(括号内是ebp前方的内容) 下一个eip
    调用leave前 14 84 80 (100,9,22),96,9 15
    15 f leave第一步 14 84 84 (100,9,22),96 15
    15 f leave第二步 14 96 84 100,9,22 16



    可以用图形表示出函数堆栈的调用关系,以03行的状态为例,调用图如下:
    这里写图片描述

    4 小结

    本文分析了函数调用过程中的堆栈变化情况,从中可以看出计算机的运行原理:
    1. 计算机中的程序采用冯诺依曼结构,即存储程序式结构。程序指令、数据均存在内存中,通过相关的寄存器进行寻址访问。
    2. C语言中的函数会被编译为汇编语言中的代码段,以顺序执行为主。不同函数在内存中有不同的入口。
    3. 执行到call语句的时候,会把程序当前状态压栈,并跳转到被调用函数的入口。执行到ret语句时,则弹栈并返回调用函数处继续执行。
    4. 在函数内部,会构建一个子堆栈,在子堆栈中存入上级堆栈的基地址,以便返回。ebp是子堆栈和传入参数的分界线。
    5. 堆栈链是函数调用运行的关键所在。

    展开全文
  • 函数调用过程中,第一个进栈的是(主函数中的)调用处的下一条指令(即函数调用语句的下一条可执行语句)的地址;然后是函数的各个参数,而大多数C/C++编译器中,函数调用的过程中,函数的参数是由右向左入栈的...
          函数调用过程中,第一个进栈的是(主函数中的)调用处的下一条指令(即函数调用语句的下一条可执行语句)的地址;然后是函数的各个参数,而在大多数C/C++编译器中,在函数调用的过程中,函数的参数是由右向左入栈的;然后是函数内部的局部变量(注意static变量是不入栈的);在函数调用结束(函数运行结束)后,局部变量最先出栈,然后是参数,最后栈顶指针指向最开始存的指令地址,程序由该点继续运行。

           函数调用方式决定了函数参数入栈的顺序,是由调用者函数还是被调用函数负责清除栈中的参数等问题,而函数名修饰规则决定了编译器使用何种名字修饰方式来区分不同的函数,如果函数之间的调用约定不匹配或者名字修饰不匹配就会产生以上的问题。
    展开全文
  • 函数调用过程中的栈帧结构及其变化

    千次阅读 多人点赞 2018-04-28 02:34:42
    前言:本文旨在从汇编代码的角度出发,分析函数调用过程中栈帧的变化。栈帧的简单介绍: 当某个函数运行时,机器需要分配一定的内存去进行函数内的各种操作,这个过程中分配的那部分栈称为栈帧。下图描述了栈帧的...
  • C++函数调用过程和内置函数详解

    千次阅读 2018-03-12 10:15:01
    上图表示函数调用过程:①程序先执行函数调用之前的语句;②流程的控制转移到被调用函数入口处,同时进行参数传递;③执行被调用函数函数体的语句;④流程返回调用函数的下一条指令处,将函数返回值带回;⑤接着...
  • C语言函数调用过程

    千次阅读 2018-04-22 18:44:44
    C语言的函数调用过程 反汇编代码 main()函数的创建 Add()函数的调用过程 main()函数的销毁 C语言的函数调用过程 先上一段代码 #include<stdio.h> int Add(int x, int y) { int z = 0; z = x...
  • c++成员函数调用过程

    千次阅读 2018-10-15 16:51:34
    成员函数调用过程 假如我们调用p->mem():   1.首先确定p的静态类型。 2.p的静态类型查找mem()。如果找不到,则依次直接基类不断查找直到继承链的顶端。 3.一旦找到了mem...
  • 关于函数调用过程中的实参和形参问题 作者:张老师,华清远见嵌入式学院讲师。 C语言是一种面向过程的语言,它的程序执行过程是按逻辑顺序执行,函数的函数体的定于位置与程序执行无关,该函数必须程序执行过程中...
  • C语言在调用一个函数func()时,如果func()接受不超过4个的参数,则调用者直接将实参从左到右依次赋值给r0, r1, r2, r3寄存器(如果是指针则传入地址),然后调用func()。 进入func()函数后,func()将自己的栈...
  • 3.C++函数调用过程

    万次阅读 2020-03-08 17:31:05
    函数调用堆栈的过程 1.函数普通变量的内存分配问题 当一个函数进行调用时,函数的形参、以及函数的局部变量都会被分配内存,而栈又分两种; 栈低不变,栈顶不断动态变化; 栈顶不变,栈低动态变化; #...
  • C函数调用过程

    千次阅读 2012-08-11 20:25:07
    这几天看GCC Inline Assembly,C代码中通过asm或__asm__嵌入一些汇编代码,如进行系统调用,使用寄存器以提高性能能,需要对函数调用过程中的堆栈帧(Stack Frame)、CPU寄存器、GCC inlie assembly等了如指掌。...
  • c函数调用过程原理及函数栈帧分析

    万次阅读 多人点赞 2013-07-24 01:49:05
    今天突然想分析一下函数相互调用过程中栈帧的变化,还是想尽量以比较清晰的思路把这一过程描述出来,关于c函数调用原理的理解是很重要的。
  • c语言中函数调用过程

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

    千次阅读 2012-07-24 09:56:55
    面试时经常遇到一些蛋疼的题目,尽管实际中从来没用到,或者大家工作过程中没有仔细思考的,今天来转帖一下这个函数调用过程,以及参数是怎么压栈出栈的: 文章来自:...
  • 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...
  • 这种形式的数据结构正好满足我们调用函数的方式: 父函数调用子函数,父函数前,子函数后;返回时,子函数先返回,父函数后返回。栈支持两种基本操作,push和pop。push将数据压入栈,pop将栈的数据弹出并存储...
  • 嵌入式软件编程,经常会用到函数调用,之前学习如何C语言嵌入汇编时有了解到C语言之前的参数调用是使用寄存器R0传递第一个参数,R1传递到第二个..一直到R3传递第四个参数.但是实际上有时可能传递的参数非常...
  • 汇编语言函数调用过程

    千次阅读 2012-10-31 21:13:48
    汇编语言函数调用过程(转) 其中cs是代码段选择寄存器,eip是偏移量寄存器 CS:EIP指向下一条指令的地址 (也叫PC)     今天看了Programming from the Ground Up的函数(Page 53)调用一章,对汇编语言函数调用有了...
  • C++函数调用过程深入分析

    千次阅读 2017-12-19 13:56:20
    0. 引言 函数调用过程实际上也就是一个中断的过程,那么C++到底是怎样实现一个函数的调用的呢?参数入栈、函数跳转、保护现场、回复现场等又是怎样实现的呢?本文将对函数调用过程进行深入的分析和详细解释,...
  • 堆栈和函数调用过程

    千次阅读 2017-10-08 21:48:16
    栈用于维护函数调用的上下文,离开了栈函数调用就没法实现。 2、 堆区(heap):一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。与数据结构的堆是两码事,分配方式类似于链表。堆是用来...
  • 函数调用堆栈的过程

    万次阅读 多人点赞 2018-05-31 11:46:43
    本篇来分析函数调用过程:通过下面一个简单的例子来进入话题:#include<stdio.h> 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-04-19 04:21:18
    函数的调用过程,栈帧的创建和销毁 ...栈帧是编译器用来实现函数调用过程的一种数据结构。C语言,每个栈帧对应着一个未运行完的函数。 以Add()函数为例研究一下函数的调用过程。 #include<st...
  • 一、C++ 源代码部分 int Add(int x,int y) { int sum; sum = x+y; return sum; } int main(int argc, char * argv[]) { int z; z = Add(1,2);...1、函数调用部分: int main(int argc, char * argv[])
  • 1.主调函数与被调函数 这是一个相对的概念,调用者叫主调函数,被调用者叫被调函数,一个函数可以是主调函数也可以是被调函数。 2.形参与实参 形式参数(简称“形参”) ...在函数调用过程中,系...
  • 函数调用过程解析图

    千次阅读 2017-08-06 15:53:43
    main函数及Add函数调用解析图 #include #include int Add(int _x, int _y) { int ret = 0; ret = _x + _y; return ret; } int main() { int x = 10; int y = 20; int ret = 0;  ret=Add(x, y); printf("%
  • 函数调用过程

    千次阅读 2017-07-18 16:35:56
    栈帧也叫过程活动记录,是编译器用来实现函数调用过程的一种数据结构。C语言,每个栈帧对应着一个未运行完的函数。从逻辑上讲,栈帧就是一个函数执行的环境:函数调用框架、函数参数、函数的局部变量、函数执行完...
  • 函数调用过程看栈帧的变化
  • Windows系统调用学习笔记(一)—— API函数调用过程前言Windows API实验:分析ReadProcessMemory第一步:定位函数第二步:开始分析 前言 一、学习自滴水编程达人中级班课程,官网:https://bcdaren.com 二、海东...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,621,557
精华内容 648,622
关键字:

在函数调用过程中