总结一下最近学习到的预处理的相关知识以及在使用Linux进行分步操作C语言后对于C程序的编译与链接过程自己的一点看法。
首先 有几个在调试方面非常有用的预处理操作符 __FILE__;__LINE__;__DATE__;__TIME__;__STDC__
__FILE__替换值是 进行编译的源文件名
__LINE__替换值是文件当前行的行号
__DATE__替换值是文件编译的日期
__TIME__替换值是文件编译的时间
这些文件可以帮助我们在调试文件的同时了解到我们对于当前所在文件行数 文件名等相关信息的了解 然我们可以更容易调整我们的错误或者BUG。
在学习宏的时候,我们会很明显地感觉到它和我们的另外一个内容很是相似,那就是函数。但是,当我们深入了解宏后我们发现还是有很大区别的。
首先我们可以直观的看出,从代码长度上来讲 宏定义 在每次使用时,宏代码都被插入到程序中,除了非常小的宏之外,程序的长度将大幅度正常,这样在使用时,系统读取时的速度一定是会受到影响,而且 当代码冗长的时候也会出现很多调试方面的问题。而函数代码只出现与一个地方,每次使用这个函数的时候,只需要调用那个地方的同一份代码。这样可以减轻系统读取的负担。所以在代码长度上来讲,使用函数或许更加实用。但是在执行速度上 宏的速度却比函数要快的多,因为函数会存在函数调用和返回的额外开销。但是 由于C中存在的优先级问题。宏参数的求值实在所有周围表达式的上下文环境里,如果不加入括号来限制,就会出现很多问题,临近操作符的优先级可能会产生不可预料的结果。但是在函数中就不会出现类似的问题。函数参数只在函数调用时求值一次,它的结果值传递给函数,表达式的求值结果更容易预测且不容易出现问题。计算的进行只会在函数内部出现,并不会出现外部影响使其出现不可预料的结果。
参数在每次用于宏定义时,他们都将会被宠幸求值,由于多次求职,具有副作用的参数可能会产生不可预料的结果。但是参数在函数被调用前只求值一次。在函数中多次使用参数并不会导致多种求值过程,参数的副作用并不会造成任何特殊的问题。所以如果当我们使用宏的时候一定要主要参数的值。如果调用出现问题一定要去看看宏的参数是否出现了问题。宏在定义参数的时候,只要对参数的值操作时合法的,他就可以使用任何参数类型。而函数就不可以,函数的参数是与类型有关的,如果参数的类型不相同,就需要使用不同的函数,否则系统会报错,或者产生随机值。
组成一个程序的每个源文件通过编译过程分别转换为目标代码,然后,各个目标文件由连接器捆绑在一起,形成一个单一而完整的可执行程序。我们可以使用Linux来观察这个一系列的过程。总体来讲程序的编译与链接分为四个过程,预处理 编译 汇编 和链接。
1、预处理
预处理是C语言程序从源代码变成可执行程序的第一步,主要是C语言编译器对各种预处理命令进行处理,包括头文件的包含、宏定义的扩展、条件编译的选择等。打印出预处理之后的结果:gcc -E xxx.c 这样我们就可以看到源代码中的各种预处理命令是如何被解释的,从而方便理解和查错。
2、编译
编译之前,C语言编译器会进行词法分析、语法分析(-fsyntax-only),接着会把源代码翻译成中间语言,即汇编语言。如果想看到这个中间结果,可以用-S选项。
编译程序工作时,先分析,后综合,从而得到目标程序。所谓分析,是指词法分析和语法分析;所谓综合是指代码优化,存储分配和代码生成。为了完成这些分析综合任务,编译程序采用对源程序进行多次扫描的办法,每次扫描集中完成一项或几项任务,也有一项任务分散到几次扫描去完成的。下面举一个四遍扫描的例子:第一遍扫描做词法分析;第二遍扫描做语法分析;第三遍扫描做代码优化和存储分配;第四遍扫描做代码生成。
值得一提的是,大多数的编译程序直接产生机器语言的目标代码,形成可执行的目标文件,但也有的编译程序则先产生汇编语言一级的符号代码文件,然后再调用汇编程序进行翻译加工处理,最后产生可执行的机器语言目标文件。
语法检查之后是翻译动作, gcc 提供了一个优化选项 -O,以便根据不同的运行平台和用户要求产生经过优化的汇编代码.
3、汇编
把作为中间结果的汇编代码翻译成了机器代码,即目标代码,不过它还不可以运行。如果要产生这一中间结果,可用gcc的-c选项,当然,也可通过as命令_汇编_汇编语言源文件来产生。
4、链接
链接是处理可重定位文件,把它们的各种符号引用和符号定义转换为可执行文件中的合适信息(一般是虚拟内存地址)的过程。链接又分为静态链接和动态链接,前者是程序开发阶段程序员用ld(gcc实际上在后台调用了ld)静态链接器手动链接的过程,而动态链接则是程序运行期间系统调用动态链接器(ld-linux.so)自动链接的过程。将编译输出文件main.o链接成最终可执行文件main.el