栈与调用惯例

先介绍一下程序的内存布局

现代的应用程序都运行在一个内存空间里,在32位的系统中,内存大小为4GB(2的32次方),整个内存是一个统一的地址空间,用户可以用一个32位的指针访问内存的任意位置。

但其实大多数操作系统会把4GB的内存空间中的一部分分给内核使用,被称为内核空间,应用程序无法直接访问。Windows下会默认把高2GB的空间分配给内核(可配置为1GB),Linux下默认将高地址的1GB空间分配给内核。剩下的空间称为用户地址空间。

用户地址空间中还有一个保留区和动态链接库映射区


栈是一个容器  FIFO  向下增长

栈在程序运行中保存了一个函数调用所需要的维护信息,称为堆栈帧或活动记录,一般包括以下内容:

函数的参数和返回值;

保存的上下文;  包括函数调用前后需要保持不变的寄存器

临时变量。

一个活动的记录一班用ebp和esp两个寄存器维护

压参的时候把所有或者一部分压入栈中,没有压入栈的参数使用某些特定的寄存器传递。

开辟出的栈会被初始化为0XCCCCCCCCh,所以我们在栈上定义一个未初始化的变量调试的时候会看到“烫烫烫...”, 0XCCCC如果被当成文本就是这个字。

int foo()
{
    return 123;
}

上面这个函数的返回值会被放入exa寄存器中  调用方可以通过读取exa寄存器来获取返回值

调用惯例

有这样一个函数

int foo(int m,int n)
{
    int a=0,b=0;
    ...
}
int main()
{
    foo(3,4);
    
}

函数的调用方和被调用方对于函数如何调用必须要有一个明确的约定,这样的约定成为调用惯例。

一般调用惯例会规定如下几个内容

函数参数的传递顺序和方式:参数的传递有很多种方式,最常见的是通过栈传递,函数的调用方将参数压入栈中,函数自己在把参数从栈中取出来。对于有多个参数的函数,调用惯例要规定参数的压栈顺序是从左还是从右压,有些惯例允许使用寄存器传值,以提高性能。

栈的维护方式:参数压栈后,函数体会被调用,此后 需要把压入栈中的参数全部弹出,以使得栈在函数调用前后保持一致

名字修饰的策略:为了链接的时候对调用管理进行区分,调用管理要对函数本身的名字进行修饰。不同的惯例有不同的修饰策略

在C语言中。存在着多个调用惯例,但默认是cdecl

函数返回值传递


exa是传递返回值的通道,函数将返回值存储在exa中,返回后函数的调用方再读取exa,但exa本身只有4个字节,对于返回5-8个字节的对象把低4个字节存储到exa中,高4个字节存储在edx中,联合返回。

大于8个字节的对象怎么存储呢?

typedef struct big_thing
{
    char buf[128];
}bigthing;

big_thing return_test()
{
    big_thing b;
    b.buf[0]=0;
    return b;
}

int main()
{
    big_thing n=return_test();
}

用伪码表示如下:

void return _test(void *tmp)
{
    big_thing b;
    b.buf[0]=0;
    memcpy(temp,&b,sizeof(big_thing));
    exa=temp;
}

int main()
{
    big_thing temp;
    big_thing n;
    return_test(&temp);
    memcpy(&n,exa,sizeof(big_thing));
}

可看出如果返回值类型的尺寸太大,C语言字函数返回时会使用一个临时的栈上内存区域作为中转,结果返回值对象会被拷贝两次,及从被调用函数的栈上的变量b拷贝到temp,再从temp到n

——————————————————摘自程序员的自我修养

时间: 2024-10-16 15:49:16

栈与调用惯例的相关文章

1.3 函数调用反汇编解析以及调用惯例案例分析

首先来段代码来瞧瞧: #include <stdio.h> int add(int x,int y){ int z; z=x+y; return z; } int main(){ int r=add(3,4); printf("%d\n",r); return 0; } 一个简单的函数调用,我们把main函数里的r=add(3,4)反汇编: 可以看到,(这里采用c默认的函数调用惯例,)首先进行参数压栈,看清楚了,是把参数从右往左压栈,然后call这个函数.跟踪,call跟进

lua 栈最后调用的函数,用于看调试信息

lua_getinfo int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); 返回一个指定的函数或函数调用的信息. 当用于取得一次函数调用的信息时,参数 ar 必须是一个有效的活动的记录.这条记录可以是前一次调用lua_getstack 得到的,或是一个钩子 (参见lua_Hook)得到的参数. 用于获取一个函数的信息时,可以把这个函数压入堆栈,然后把 what 字符串以字符 '>' 起头.(这个情况下,lua_geti

嵌入式开发学习(9)&lt;汇编写启动代码之设置栈和调用c语言&gt;

C语言运行时需要和栈的意义: "C语言运行时(runtime)"需要一定的条件,这些条件由汇编来提供.C语言运行时主要是需要栈 C语言与栈的关系:C语言中的局部变量都是用栈来实现的.如果我们汇编部分没有给C部分预先设置合理合法的栈地址,那么C代码中定义的局部变量就会落空,整个程序就死掉了. 我们平时在编写单片机程序(譬如51单片机)或者编写应用程序时并没有去设置栈,但是C程序还是可以运行的.原因是:在单片机中由硬件初始化时提供了一个默认可用的栈,在应用程序中我们编写的C程序其实并不是全

链接,转载与库:GUIDELINE

第一章 1.2 万变不离其宗 -->各种平台软硬件差异大,但基本概念和原理是一样 SO,只需掌握一种平台上的技术,就可以举一反三. 我们要掌握的是 X86平台下 系统软件 背后的机理. -->介绍硬件结构框架 1.3 站得高,望得远 -->系统软件的概念 -->系统软件的结构--层 1.4 操作系统主要做什么:提供抽象接口+管理硬件资源 -->硬件资源(1.CPU:2.Memory&Disk:3.I/O设备)是有限的,如何发挥硬件的全部能力 -->(1.4.1)

程序员的自我修养—链接、装载与库pdf

下载地址:网盘下载 内容简介 编辑 <程序员的自我修养:链接.装载与库>对装载.链接和库进行了深入浅出的剖析,并且辅以大量的例子和图表,可以作为计算机软件专业和其他相关专业大学本科高年级学生深入学习系统软件的参考书.同时,还可作为各行业从事软件开发的工程师.研究人员以及其他对系统软件实现机制和技术感兴趣者的自学教材. 媒体评论 编辑 这是一本深人阐述链接.装载和库等问题的优秀图书,读来让人愉悦,你从巾可以清晰地了解程序的前世今生,彻底理解敲人的代码如何变成程序任系统中运行.通读本书不管对于开发

Android各种获取代码调用栈的方法[补]

打印调用栈不用说,基本上每位开发者都会用到,讨论几个方法,以前也说过,http://blog.csdn.net/freshui/article/details/9456889 再次简单整理一下吧,啰嗦就啰嗦了 :) 基本分两大类,一类是静态的,要把打印语句插入到代码中,一类是动态的,需要看的时候,查看一下,实时观测各线程调用栈情况. 静态方法 1. Java中打印调用栈 比较简单,利用Throwable,直接log中打印出来: [java] view plain copy Log.d(TAG, 

程序的内存布局——函数调用栈的那点事

[注]此文是<程序员的自我修养>的读书总结,其中掺杂着一些个人的理解,若有不对,欢迎拍砖. 程序的内存布局 现代的应用程序都运行在一个虚拟内存空间里,在32位的系统里,这个内存空间拥有4GB的寻址能力.现代的应用程序可以直接使用32位的地址进行寻址,整个内存是一个统一的地址空间,用户可以使用一个32位的指针访问任意内存位置. 在进程的不同地址区间上有着不同的地位,Windows在默认情况下会将高地址的2GB空间分配给内核,而Linux默认将高地址的1GB空间分配给内核,具体的内存布局如下图:

如何动态调用 C 函数

JSPatch 支持了动态调用 C 函数,无需在编译前桥接每个要调用的 C 函数,只需要在 JS 里调用前声明下这个函数,就可以直接调用: 我们一步步来看看怎样可以做到动态调用 C 函数. 函数地址 首先若要动态调用 C 函数,第一步就是需要通过传入一个函数名字符串找到这个函数地址,这里一个必要的前提条件就是 C 编译后的可执行文件里必须有原函数名的信息,才有可能做到通过函数名字符串找到函数地址.我们写个简单的程序来看看它编译后可执行文件的内容有没有这个信息: 编译这个文件,并用otool看下它

JVM学习(2)——技术文章里常说的堆,栈,堆栈到底是什么,从os的角度总结--转载http://www.cnblogs.com/kubixuesheng/p/5202561.html

转载自---http://www.cnblogs.com/kubixuesheng/p/5202561.html 俗话说,自己写的代码,6个月后也是别人的代码--复习!复习!复习!涉及到的知识点总结如下: 堆栈是栈 JVM栈和本地方法栈划分 Java中的堆,栈和c/c++中的堆,栈 数据结构层面的堆,栈 os层面的堆,栈 JVM的堆,栈和os如何对应 为啥方法的调用需要栈 属于月经问题了,正好碰上有人问我这类比较基础的知识,无奈我自觉回答不是有效果,现在深入浅出的总结下: 前一篇文章总结了:JV