您的位置:首页 > 编程语言

一站式编程笔记(17,18,19)

2013-10-29 22:32 288 查看
第17章:

汇编器把文本文件转换成目标文件.o;目标文件由若干个Section组成,我们在汇编程序中声明的.section会成为目标文件中的Section

然后链接器把目标文件的Section合并成几个Segment,生成可执行文件。

最后加载器根据可执行文件的Segment信息加载运行这个程序。

第18章:

函数调用解析:

在进入main()中,在调用的函数的时候,先把局部变量和函数所需要的变量保存起来,通过push压栈来保存。

注:其实在进入main中,ebp就已经被压入堆栈了,以后要寻找变量都是依靠ebp来寻找的,因为esp是随时变化的。

另外函数的参数都是从右到左压入堆栈的,调用函数之后再把调用函数之后的下一条指令的地址压入堆栈,然后执行call指令

然后修改eip的值,跳转到调用函数执行。

在调用函数中:

先把ebp压栈,注意,这里是main函数的ebp。另外在这里esp的值减了4(这步是自动完成),因为在push和pop中esp是根据此来变化的。

然后把esp的值给ebp,那么这个函数的ebp值也已经保存了。

我的理解是:ebp和esp是一一对应的,在设置好ebp值后,你mov %esp就是往堆栈中保存数据了,相当于push。并且调用函数的ebp的值才需要push,而本函数的ebp 需要赋值就好,不需要压栈!

返回的时候就是个逆过程,把ebp的值给esp,然后通过ret指令,esp值增加4。回到了main函数。

const只读的,一旦确定不能修改,并且一定要初始化;

栈是从高地址向低地址增长,但数组却是从低向高排列。

register 并没有在栈上分配空间,而是直接在ebx寄存。

结构体内存布局:

栈是从高地址向低地址增长的,但是结构体成员却是低地址向高地址排列的。和数组不同的是,结构体成员并不是一个接一个的,中间有空隙,称为填充。是因为编译器在考虑在安排各种变量的地址时会考虑到对齐问题。(即指令的起始地址应该是4或者2的整数倍);

不懂的是为什么编译器要这么存放数组和结构体呢?是出于什么原因考虑的。。。

联合体:一个联合体的各个成员占用相同的内存,联合体的长度等于其中最长成员的长度。至于在内存中时如何存放的呢,其实这个无关紧要,因为我们如果看成是比特,或者看成是字节都可以的。就是说我们看联合体成员,可以换一种视角,来知道内存的布局;

第19章:

主函数中包含“*.h”是因为.h文件包含了其他.c文件定义的函数,这里只是将这些默认的链接属性为extern的函数组合在一起,放到.h文件中,然后给main.c文件来用。 这里为什么要这么组织呢?

是因为.h文件只是声明,并不是定义。 而如果main和foo都包括stack.c的时候,还有一个函数想将main.c和foo.c链接在一起的时候就不好办了。因为不能重复定义,但是可以重复声明。

链接共享库和链接静态库的区别:

静态库:把一组代码编译成一个库,可以用自定义的代码组成;

组成静态库需要用到ar打包命令,静态库以.a作为后缀。

编译器在链接时,先找共享库另外再去寻找静态库。如果只考虑静态库,可以指定-static选项。

要查看是不是链接时用到了静态还是其他,可以用objdump -d main 将可执行文件反汇编成汇编文件,然后查看相应的汇编文件。

共享库:

组成共享库时在编译的时候需要加-fPIC.这个共享库一般出现在C语言的库文件中!

共享库(动态)时是只确定地址,在运行时才做动态链接; 而静态链接库时,链接器会把静态库中的目标文件取出和真正的可执行文件真正的链接在一起。

我们一般用的库就是静态库,这样不需要的目标文件其实是不需要做链接的。这样的另一个好处是链接时只要写一个库文件名。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: