计算机世界--函数调用

来源:互联网 发布:张国荣同性恋 知乎 编辑:程序博客网 时间:2024/06/18 00:00

汇编和可执行文件

编译

链接

汇编代码

1.汇编和可执行文件

我们知道:汇编≈机器码,汇编是用来给我们人类看的。那么可执行文件是怎么生成的呢?它是由源代码通过编译,得到汇编代码,汇编代码通过链接,得到可执行文件。

我们在写代码的时候,我们需要时候各种各样的API,也是就是说我们的代码中90%的代码并没有与之对应的汇编代码,那么什么是没有与之对应的代码呢?假设在我们的代码中,有一句很简单的语句,printf函数,那么我们要实现这句代码的时候,它需要做很多事情,比如说要与各种各样的显卡交互,而我们软件工程师只需要管软件的事情,并不需要管硬件的事情,因为中间恰好有一个东西叫做操作系统。而我们是没有硬件上的汇编代码,如果真的没有硬件汇编代码,那么我们的代码将变得没有任何意义。所以,如果你让是想在不同的操作系统中运行这句代码,那么你需要根据不同的操作系统的规则来Link(连接)硬件汇编代码。

link:就是把操作系统提供的与硬件相关的汇编代码,与我们的自己写的代码链接到一起的过程。

其实在任何一个操作系统下面编译都是这么一回事,首先会产生一个obj的文件,即程序产生的汇编代码,这个汇编代码是没有意义的,因为它并不能被操作系统执行,它不符合操作系统的要求,还需要进行Link才能达到操作系统的要求。

所以从源代码到可执行文件,中间需要经过两步,第一步是编译,第二步是Link(生成与平台对应的文件格式),Link的时候,相当于深加工(比如说Windows平台下,我们加上PE结构,比如说加载dll或者函数),这样Windows平台下面的可执行文件就生成了。

编译器:其实就是做一样事情,即将c/c++代码变成与之对应的汇编代码,这种对应关系是非常稳定和可靠的。所以平时我们的程序会非常的稳定,但是,也会有例外的情况,这种例外的情况指的是什么呢?我们的编译器会对我们的程序进行一些优化,就是将我们没有用的代码给去掉,也有可能根据我们生成模式的不同,编译器可能生成不同的代码,比如说debug版本和release版本。归根结底,就是编译器对我们的程序进行了一些优化。

所谓编译器,就是将我们的复杂逻辑,分解成简单的逻辑,我们平时写的代码中会有很多种逻辑,但是到了汇编中,可能就会有这样几种逻辑:1、赋值语句;2、跳转语句;3、计算语句

汇编代码包括:

1、赋值语句;

2、跳转语句

3、计算语句

赋值语句; 例如: mov (移动) 目标 , 源 临时的变量 临时变量是否占用内存空间呢?肯定是占用的,程序里面的任何东西都要占用内存,而临时变量占用的内存,我们称之为栈;

我们会整体的将我们的内存划分为四大块,分别为:栈、堆、代码、常量。

栈:读写都可以

堆:读写都可以

代码:只读不写

常量区:只读不写

为什么要进行这样的一个分区呢?因为这样会更加的安全,为什么会更加安全呢?无论是从CPU还是内存角度来看,这些数据都是一模一样的,这样的话,如果是一门能够操作内存的语言,比如c/c++,万一不小心把指针指向了代码区,而且不小心的把它给修改了,那么这样程序就会挂了。所以我们将代码区划分出来,并且是受保护的,这样即使指向了代码区,那么这个指针是不能指向这个区的,就会报错。

栈区是用来做什么呢?在Windows下默认为我们的每一个程序会生成1M的内存,但是这也是可增长的,以后我们会经常碰到一个问题,就是栈溢出,比如 char array[1024*1024] = {0};//这样就会栈溢出的错误,解决的办法是,在堆中申请,比如 char *p = new char[1024*1024]; // 这样就不会出错了

栈会为我们做什么呢?它会存储一些临时变量,因为临时变量都会占用内存的,而申请内存,释放内存,都需要占用一定时间的,导致效率降低,所以干脆在我们的程序里面直接申请一片内存,作为栈控件,而这一片空间在程序运行的过程中都不会释放,直到程序运行完成之后,来自动释放这片内存。

原理:用栈寄存器 ebp(游标) 来记录栈底的位置,然后每一次需要使用新的变量的时候,我在使用另外一个栈寄存器 esp 来记录栈的最高点,当我们使用完成之后,将最高点的游标和最低点的游标相等,也就是将这一段区间的数据作废,就相当于我们的临时变量被释放掉了。这就解释了为什么栈的效率高,因为它能够反复的这片内存空间。

当然了,栈不仅仅是用来存储临时变量的,数据回溯,传递参数,函数地址等等。比如说,保存跳转后返回地址,每次调用函数(API)

函数执行的汇编过程(有时间再补吧)

1 0
原创粉丝点击