汇编与C的关系

汇编程序

.section指示把代码划分成若干个段(Section),程序被操作系统加载执行时,每个段被加载到不同的地址,具有不同的读、写、执行权限。

.data保存程序数据,可读可写  全局变量属于.data

.text保存代码,只读和可执行

.plt段协助完成动态链接的过程

 

内存寻址:

直接寻址、变址寻址、间接、基址寻址、立即数寻址、寄存器寻址

程序中的地址都是虚拟地址

 

ELF文件:

UNIX系统的可执行文件都采用ELF格式,有三种类型:可重定位的目标文件Relocatable、可执行文件Executable、共享库Shared Object

 

如果目标文件是由C代码编译生成的,用gcc做链接就没错了,整个程序的入口点是crt1.o中提供的_start,它首先做一些初始化工作(以下称为启动例程,Startup Routine),然后调用C代码中提供的main函数。所以,以前我们说main函数是程序的入口点其实不准确,_start才是真正的入口点,而main函数是被_start调用的。

 

链接:静态链接(编译时) 动态链接(运行时)

 

1. 操作系统在加载执行main这个程序时,首先查看它有没有需要动态链接的未定义符号。

2. 如果需要做动态链接,就查看这个程序指定了哪些共享库(我们用-lc指定了libc)以及用什么动态链接器来做动态链接(我们用-dynamic-linker /lib/ld-linux.so.2指定了动态链接器)。

3. 动态链接器在共享库中查找这些符号的定义,完成链接过程。

 

变量的存储布局

作用域(Scope)这个概念适用于所有标识符,而不仅仅是变量,C语言的作用域分为以下几类:函数作用域(Function Scope),标识符在整个函数中都有效。只有语句标号属于函数作用域。标号在函数中不需要先声明后使用,在前面用一个goto语句也可以跳转到后面的某个标号,但仅限于同一个函数之中。

文件作用域(File Scope),标识符从它声明的位置开始直到这个程序文件[27]的末尾都有效。例如上例中main函数外面的A、a、b、c,还有main也算,printf其实是在stdio.h中声明的,被包含到这个程序文件中了,所以也算文件作用域的。

块作用域(Block Scope),标识符位于一对{}括号中(函数体或语句块),从它声明的位置开始到右}括号之间有效。例如上例中main函数里的a、b、c。此外,函数定义中的形参也算块作用域的,从声明的位置开始到函数末尾之间有效。

函数原型作用域(Function Prototype Scope),标识符出现在函数原型中,这个函数原型只是一个声明而不是定义(没有函数体),那么标识符从声明的位置开始到在这个原型末尾之间有效。例如int foo(int a, int b);中的a和b。

 

对属于同一命名空间(Name Space)的重名标识符,内层作用域的标识符将覆盖外层作用域的标识符,例如局部变量名在它的函数中将覆盖重名的全局变量。命名空间可分为以下几类:语句标号单独属于一个命名空间。例如在函数中局部变量和语句标号可以重名,互不影响。由于使用标号的语法和使用其它标识符的语法都不一样,编译器不会把它和别的标识符弄混。

struct,enum和union(下一节介绍union)的类型Tag属于一个命名空间。由于Tag前面总是带struct,enum或union关键字,所以编译器不会把它和别的标识符弄混。

struct和union的成员名属于一个命名空间。由于成员名总是通过.或->运算符来访问而不会单独使用,所以编译器不会把它和别的标识符弄混。

所有其它标识符,例如变量名、函数名、宏定义、typedef的类型名、enum成员等等都属于同一个命名空间。如果有重名的话,宏定义覆盖所有其它标识符,因为它在预处理阶段而不是编译阶段处理,除了宏定义之外其它几类标识符按上面所说的规则处理,内层作用域覆盖外层作用域。

 

结构体的长度等于所有成员长度之和+填充字节

联合体的长度等于其中最长成员的长度(各个成员占用相同的内存空间)

volatile限定符修饰变量,就是告诉编译器,即使在编译时指定了优化选项,每次读这个变量仍然要老老实实从内存读取,每次写这个变量也仍然要老老实实写回内存,不能省略任何步骤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章