好久不用,很多内容都忘了。刚刚复习了一下,再写一遍:32位x86系统C函数调用时的内存分布。
每个C语言函数在运行过程中,在栈顶对应一个栈帧。想想堆栈是一个地址由上往下增长的线性内存(为什么要由上往下增长?后面说明),栈帧的顶端地址位于esp寄存器,栈帧底部地址由ebp寄存器保存(当然,这个说法不准确,ebp再往下还有这个函数的参数,后面详细说明)。结构如下所示(每行4字节):
00 临时变量2 <---- esp
04 临时变量1
08 局部变量2
0C 局部变量1
10 调用函数ebp指针值 <---- ebp
14 调用函数下一条执行指令地址
18 参数1
1C 参数2
可以看出,esp指向栈顶,ebp指向调用函数的ebp值: *esp = (first temp variable), *ebp = (last function's ebp),第一个参数是*(ebp+8),上一个函数中的返回指令是*(ebp+4)
在函数调用前,esp指向参数1(0x18),执行call命令后,esp指向调用函数下一条执行指令地址(0x14),被调用函数首先执行如下指令:
pushl %ebp
movl %esp, %ebp
此时 esp=ebp=0x10。
ok,回到刚才的问题,为什么要从上往下想象地址空间。这种想象地址空间的能力非常重要,这个搞不好,在学汇编和读汇编时会比较郁闷。
一般我们想象一个栈的时候,要么从上往下增长,要么从下往上增长,或者从左往右、从右往左增长。不过一般push时我们感觉是“压进去”,pop时感觉是“弹出来”,这样从上往下增长的话,感觉比较怪异:往上面压进去,从上午往下弹出来。而水平压进去、弹出来的话,展示栈中的内容展示不了多少,因为栈中数字一般是四字节(对于32位系统)一个数据,水平写栈中内容的话可能就是如下的样子:
0x12345678 0x87654321 0x12345678 0x87654321 0x12345678 0x87654321
一行写不了几个数据,并且还看着比较不舒服。所以,栈从下往上涨是想起来最舒服的。
而内存空间为什么地址从上往下依次增大想象起来更舒服一些呢?首先,栈一般都是从大地址往小地址增长的,按照栈从下往上增长的方向,内存地址当然要从上往下增长了。另外,C语言中的结构(struct)都是从上往下写的,而一般上面的地址比下面的地址要小,更靠前,因此地址从上往下想时也更加符合平时看着的习惯。
分享到:
相关推荐
总结来说,C语言函数调用的底层机制主要包括以下几个步骤: 1. 参数通过堆栈传递,遵循从右向左的顺序。 2. `call` 指令调用函数,并将返回地址压栈。 3. 函数内部创建栈帧,分配局部变量空间。 4. 函数执行,计算...
C语言函数调用是编程中的基础操作,它涉及到编译器、链接器和运行时环境的协同工作。本文将深入探讨C语言函数调用的详细过程,以最简单的C代码为例,结合X86汇编语言进行分析。 首先,我们看一段简单的C代码: ```...
系统调用与系统函数调用是操作系统中至关重要的概念,它们是用户程序与操作系统交互的主要方式。在计算机科学中,当一个应用程序需要执行只有操作系统才能提供的服务时,比如磁盘I/O、进程管理或者网络通信,它就...
以Intel x86系列CPU为例子。讲解详细深入。看完后C语言水平能够进入一个新台阶。
在编译和链接这些文件时,C编译器(如GCC)和汇编器(如NASM)需要正确地处理函数调用的接口。通常,我们需要先用汇编器将`myadd.asm`编译为对象文件,再用链接器将其与C编译后的对象文件链接在一起,生成可执行文件...
4. **调用子程序**:在C代码中,我们可以通过函数调用来使用汇编子程序,如下所示: ```c int result = add(5, 7); // 调用汇编子程序进行加法 printf("Result: %d\n", result); ``` 通过这种方式,我们可以...
1. 函数指针:函数指针是指向函数的指针,可以用来调用函数。在这个笔记中,我们定义了一个函数指针pOLDMessageBoxW,用于存储MessageBoxW函数的入口点地址。 2. 函数劫持:函数劫持是指在程序运行时,动态地替换或...
总之,实现x86平台上的`printf`函数是一个涉及C语言编程、x86汇编语言、系统调用、内存管理和错误处理等多个方面的综合任务。这不仅是提升编程技能的好机会,也是对计算机系统底层工作原理的深入探索。通过这样的...
首先,C语言函数调用机制主要依赖于栈来传递参数和管理函数执行过程。栈帧(Stack frame)是每个函数调用在栈上创建的一个结构,它包含了函数参数、返回地址、局部变量和可能保存的寄存器值。在Intel 80x86架构的CPU...
首先,我们需要理解X86架构的函数调用约定。在64位X86架构中,前六个基础类型(如long、int、short、char和pointer)的参数是通过特定的寄存器(rdi、rsi、rdx、rcx、r8和r9)来传递的,而第七个参数及以后的参数则...
系统调用是一种特殊的子程序,由操作系统内核提供,用户程序通过特定的接口(通常在C语言中是`syscall()`函数)调用。当执行系统调用时,处理器会切换到内核模式,执行相应的内核代码,然后返回到用户模式,确保了...
在计算机科学中,函数调用是程序执行流程中的核心组成部分,尤其是在C语言中。本文将深入探讨C函数调用过程的原理以及函数栈帧的工作机制,以帮助理解x86架构下的函数调用细节。 首先,栈是一种特殊的数据结构,...
在软件开发过程中,尤其是涉及到C和C++编程时,理解函数调用约定是至关重要的。函数调用约定,又称调用约定或调用约定规则,是编程语言中规定如何传递参数到函数,以及如何返回结果的规则。这些规则在不同环境和编程...
4. **更新系统调用表**:在对应的架构目录下,更新系统调用表,例如在x86-64上,修改`arch/x86/kernel/entry_64.S`中的`sys_call_table`数组,将新系统的调用号与你的`sys_*()`函数关联。 5. **编译和测试**:修改...
C语言是一种底层编程语言,被广泛用于操作系统、嵌入式系统和各种软件开发。它的语法简洁,性能高效,是实现MQTT客户端的理想选择。 Paho MQTT C库提供了API,用于连接MQTT服务器(也称为broker),发布和订阅消息...
《C语言调用MATLAB程序》 在计算机编程领域,有时候我们需要在C语言工程中调用MATLAB程序,以利用MATLAB强大的数值计算和数据处理能力。本文将详细介绍如何在C语言环境中集成并运行MATLAB代码。 首先,为了在C语言...
当你不再需要某块内存时,调用`free()`将它归还给系统,防止内存泄漏。例如,`free(ptr)`会释放`ptr`所指向的内存,`ptr`应是`malloc()`的返回值。 内存分配涉及到物理内存和虚拟内存的概念。在实际的计算机系统中...
当程序运行时,操作系统负责在需要时加载DLL,并将函数调用转发给DLL中的相应函数。为了调用DLL中的函数,我们必须知道函数的名称和参数列表。 1. **声明DLL函数**:在VC++项目中,我们通常在头文件(如loaddll.h)...