程序员的自我修养 读后总结
2014-10-07 19:57
162 查看
A,对程序进行构建,一共经历了如下四个步骤,
即为预处理,编译,汇编,以及链接。
下面分别介绍
一、预编译
源文件 hello.c--->hello.i
预编译过程主要处理那些源代码文件中以 “#” 开始的预编译命令,例如“#incluide”、“#define” 等,主要规则如下:
1,将所有 "#define" 删除,并且展开所有的宏定义
2,处理所有条件预编译指令,例如 “#if‘,”#ifdef“,”#elif“, "#else", "endif"
3,处理 ”#include“预编译指令,将被包含的文件插入到该预编译指令的位置,注意,这个过程是递归执行的,也就是说被包含的文件也有可能包含其他文件
4,删除所有的注释 ”//’ “/* */”
5,添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号
6,保留所有的 #pragma 编译器指令,因为编译器需要使用它们
源文件 hello.c 经过预编译后的 hello.i 文件不包含任何宏定义 ,因为所有的宏已经被展开,并且包含的文件也已经被插入到 hello.i 文件中
二、编译
hello.i--->hello.s
编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件,这个过程是整个程序构建的核心部分,也是最复杂的部分之一。
1,词法分析
源代码程序被输入扫描器,扫描器运用一种类似有限状态机的算法可以很轻松地将源代码的字符序列分割成一系列的记号,记号一般可以分为如下几类:关键字,标识符,数字,字符串和加号,等号等。
2,语法分析
对上面产生的记号进行语法分析,产生语法树,也就是由语法生成器生成的树就是以表达式为节点的树。
3,语义分析
语法分析仅完成了对表达式语法层面的分析,但是它并不了解这个语句是否真正有意义。例如C中两个指针做乘法运算是没有意义的,但这个语句在语法上是合法的。编译器能够分析的语义是静态语义,就是在编译期可以确定的语义,与之对应的是动态语义,就是只有在运行期才能确定的语义。
静态语义通常包括声明和类型的匹配,类型的转换。
动态语义一般指在运行期间出现的语义相关问题,比如将 0 作为除数是运行期语义错误。
经过语义分析后,整个语法树的表达式都被标识了类型。
4,中间语言生成
现代的编译器有着很多优化层次,往往在源码级别会有一个优化过程,例如2+4被6代替。源码级优化器会在源码级别进行优化,将整个语法树转换为中间代码,已经非常接近目标代代码了。其一般跟目标机器和运行时环境是无关的,比如它不包括数据的尺寸,变量地址和寄存器的名字等。
5,目标代码生成与优化
此时编译进入编译器后端,包括代码生成器和目标代码优化器。
代码生成器将中间代码转换成目标机器代码,这个过程很依赖目标机器,因为不同机器有着不同的字长,寄存器,整数数据类型和浮点数数据类型等。
之后优化器进行优化,例如选择合适的寻址方式,使用移位代替乘法运算等。
三、汇编
hello.s--->hello.o
汇编器是将汇编代码转变成机器刘可义执行的指令,每一个汇编语句几乎都对应一条机器指令。所以汇编器的汇编过程对于相对于编译器来讲比较简单,它没有复杂的语法,也没有语义,也不需要做指令优化。
四、静态链接---模块拼装
链接将上面得到的obj文件进行拼装。即人们把每个源代码块独立的编译,然后按照需要将它们组装起来,这个组装的过车个就是链接。
主要任务就是把各个模块之间相互引用的部分都处理好,使得各个模块之间能够正确的衔接。
链接过程主要包括地址和空间分配,符号决议和重定位。
最近本的静态链接过程为:每个模块的源代码文件(如 c)文件经过编译器编译成目标文件(一半扩展名为 .o 或 .obj),目标文件和库一起链接形成最终的可执行文件
上述四个过程即为程序构建的过程,最后的静态链接与动态链接对应,动态链接是运行时才对模块经行链接。
B,extern
1,extern "C
"在C++中为了对部分代码按照C语言的形式进行编译,需要利用extern
例如需要对getResult()函数按照C语言的形式进行编译
2,extern int a;
同一个工程中有两个文件,变量 a 在file1.c 中是全局变量,在file2.c 中为了使用 a 需要在file2.c中添加一行代码,如下
C,栈与堆
栈与堆均属于内存中的一部分。
1,栈
几乎每一个程序都使用栈,没有栈就没有函数,没有局部变量,也就没有我们能够看见的计算机语言。栈被定义为一个特殊容器,用户可以将数据压入栈中,也可以将栈中的数据弹出,遵守先进后出规则。
每个线程的栈都是独立的,所以一个进程有多少线程就有多少个栈,对于windows来说,每个线程默认的栈大小是1MB,在线程启动时,系统会为它在进程地址空间中分配相应空间作为栈。
栈在程序运行中具有举足轻重的地位。栈保存了一个函数调用所需要的维护信息,包括如下内容:
函数的返回地址和参数
临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量
保存的上下文:包括在函数调用前后需要保持不变的寄存器
2,堆
堆是一块巨大的内存空间,常常占据整个虚拟空间的绝大部分。在这块空间中,程序可以请求一块连续的内存,并自由地使用,这块内存在程序主动放弃之前都会一直保持有效。
对分配是按照空闲链表方法,将此时空闲的空间块用链表连接在一起,需要时进行分配
即为预处理,编译,汇编,以及链接。
下面分别介绍
一、预编译
源文件 hello.c--->hello.i
预编译过程主要处理那些源代码文件中以 “#” 开始的预编译命令,例如“#incluide”、“#define” 等,主要规则如下:
1,将所有 "#define" 删除,并且展开所有的宏定义
2,处理所有条件预编译指令,例如 “#if‘,”#ifdef“,”#elif“, "#else", "endif"
3,处理 ”#include“预编译指令,将被包含的文件插入到该预编译指令的位置,注意,这个过程是递归执行的,也就是说被包含的文件也有可能包含其他文件
4,删除所有的注释 ”//’ “/* */”
5,添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号
6,保留所有的 #pragma 编译器指令,因为编译器需要使用它们
源文件 hello.c 经过预编译后的 hello.i 文件不包含任何宏定义 ,因为所有的宏已经被展开,并且包含的文件也已经被插入到 hello.i 文件中
二、编译
hello.i--->hello.s
编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件,这个过程是整个程序构建的核心部分,也是最复杂的部分之一。
1,词法分析
源代码程序被输入扫描器,扫描器运用一种类似有限状态机的算法可以很轻松地将源代码的字符序列分割成一系列的记号,记号一般可以分为如下几类:关键字,标识符,数字,字符串和加号,等号等。
2,语法分析
对上面产生的记号进行语法分析,产生语法树,也就是由语法生成器生成的树就是以表达式为节点的树。
3,语义分析
语法分析仅完成了对表达式语法层面的分析,但是它并不了解这个语句是否真正有意义。例如C中两个指针做乘法运算是没有意义的,但这个语句在语法上是合法的。编译器能够分析的语义是静态语义,就是在编译期可以确定的语义,与之对应的是动态语义,就是只有在运行期才能确定的语义。
静态语义通常包括声明和类型的匹配,类型的转换。
动态语义一般指在运行期间出现的语义相关问题,比如将 0 作为除数是运行期语义错误。
经过语义分析后,整个语法树的表达式都被标识了类型。
4,中间语言生成
现代的编译器有着很多优化层次,往往在源码级别会有一个优化过程,例如2+4被6代替。源码级优化器会在源码级别进行优化,将整个语法树转换为中间代码,已经非常接近目标代代码了。其一般跟目标机器和运行时环境是无关的,比如它不包括数据的尺寸,变量地址和寄存器的名字等。
5,目标代码生成与优化
此时编译进入编译器后端,包括代码生成器和目标代码优化器。
代码生成器将中间代码转换成目标机器代码,这个过程很依赖目标机器,因为不同机器有着不同的字长,寄存器,整数数据类型和浮点数数据类型等。
之后优化器进行优化,例如选择合适的寻址方式,使用移位代替乘法运算等。
三、汇编
hello.s--->hello.o
汇编器是将汇编代码转变成机器刘可义执行的指令,每一个汇编语句几乎都对应一条机器指令。所以汇编器的汇编过程对于相对于编译器来讲比较简单,它没有复杂的语法,也没有语义,也不需要做指令优化。
四、静态链接---模块拼装
链接将上面得到的obj文件进行拼装。即人们把每个源代码块独立的编译,然后按照需要将它们组装起来,这个组装的过车个就是链接。
主要任务就是把各个模块之间相互引用的部分都处理好,使得各个模块之间能够正确的衔接。
链接过程主要包括地址和空间分配,符号决议和重定位。
最近本的静态链接过程为:每个模块的源代码文件(如 c)文件经过编译器编译成目标文件(一半扩展名为 .o 或 .obj),目标文件和库一起链接形成最终的可执行文件
上述四个过程即为程序构建的过程,最后的静态链接与动态链接对应,动态链接是运行时才对模块经行链接。
B,extern
1,extern "C
"在C++中为了对部分代码按照C语言的形式进行编译,需要利用extern
例如需要对getResult()函数按照C语言的形式进行编译
#ifdef _cplusplus extern "C" { #endif void getResult(){} #ifdef _cplusplus } #ednif
2,extern int a;
同一个工程中有两个文件,变量 a 在file1.c 中是全局变量,在file2.c 中为了使用 a 需要在file2.c中添加一行代码,如下
/* file1.c */ int a = 2; /* file2.c */ extern int a; void sample() { int c = a; }
C,栈与堆
栈与堆均属于内存中的一部分。
1,栈
几乎每一个程序都使用栈,没有栈就没有函数,没有局部变量,也就没有我们能够看见的计算机语言。栈被定义为一个特殊容器,用户可以将数据压入栈中,也可以将栈中的数据弹出,遵守先进后出规则。
每个线程的栈都是独立的,所以一个进程有多少线程就有多少个栈,对于windows来说,每个线程默认的栈大小是1MB,在线程启动时,系统会为它在进程地址空间中分配相应空间作为栈。
栈在程序运行中具有举足轻重的地位。栈保存了一个函数调用所需要的维护信息,包括如下内容:
函数的返回地址和参数
临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量
保存的上下文:包括在函数调用前后需要保持不变的寄存器
2,堆
堆是一块巨大的内存空间,常常占据整个虚拟空间的绝大部分。在这块空间中,程序可以请求一块连续的内存,并自由地使用,这块内存在程序主动放弃之前都会一直保持有效。
对分配是按照空闲链表方法,将此时空闲的空间块用链表连接在一起,需要时进行分配
相关文章推荐
- 《征服ASP.NET2.0AJAX》一书的读后总结(16-18章)
- 与熊共舞-读后-个人总结 waltzing with bears
- 《征服ASP.NET2.0AJAX》一书的读后总结(21-22章)
- 李开复:Windows Vista研发失败故事 读后总结
- 《程序员的自我修养》总结(一)
- 《VC++技术内幕》读后总结
- 《征服ASP.NET2.0AJAX》一书的读后总结(19-20章)
- 《征服ASP.NET2.0AJAX》一书的读后总结(1-7章) 收藏
- 周鸿祎:马化腾的成功在于打动人心 读后总结
- 学习经验(读后总结)
- Java与模式读后总结--第3章 软件的可维护性和可复用性
- Java与模式读后总结--结构模式
- Think In Java 读后总结
- Java与模式读后总结--第4章 “开-闭”原则
- STL Tutorial Reference 读后总结
- 《征服ASP.NET2.0AJAX》一书的读后总结(8-14章)
- 《征服ASP.NET2.0AJAX》一书的读后总结(21-22章)
- 《征服ASP.NET2.0AJAX》一书的读后总结(16-18章)
- java servlet and jsp cookbook 第一章 读后总结
- 《程序员的自我修养》总结(二)