C程序的编译过程由几个阶段组成:预处理(preprocessing)、编译(compilation)、汇编(assembling)、链接(linking)。这种历史性的分阶段方法有助于将责任划分给不同的工具,以简化构建过程的维护和配置。
问题:如果不理解每个阶段的工作原理,可能会遇到"未定义引用"、代码重复、不明显的错误(由于宏的不当使用)以及在代码扩展到多个文件时出现的问题。
解决方案:需要理解每个阶段执行的具体功能:预处理器处理# define、#include等指令,编译器将源C代码转为汇编代码,汇编器将其转为机器代码,链接器将所有目标文件和库合并为最终可执行文件。
代码示例:
原始代码片段:
#include <stdio.h> #define PI 3.14 int main() { printf("%f ", PI); return 0; }
显式指明阶段的gcc调用示例:
gcc -E program.c # 预处理 gcc -S program.c # 编译(到汇编) gcc -c program.c # 汇编(到目标文件) gcc program.o -o prog # 链接(linking)
关键特点:
在编译阶段,#include "file.h"指令做什么?
#Include在编译的预处理阶段直接将文件内容插入调用位置。在重复包含同一文件时,这可能导致对象的多重定义或链接错误。
在一个文件中定义函数是否足以在另一个文件中使用?
不是的,必须在头文件中声明(原型)或至少使用extern声明。否则在链接时可能会出现"隐式声明函数"或"未定义引用"等错误。
可以从另一个文件使用声明为static的变量吗?
不可以。static限制了变量或函数在当前文件的可见性。这意味着这些符号在其他目标文件中对链接器不可见。
初学者在两个源文件中实现同名的函数。在链接阶段会出现奇怪的错误"函数的多重定义"。
优点:
缺点:
创建仅用于声明的.h文件,创建仅用于定义的.c文件。使用#ifdef保护头文件。所有文件通过链接器连接成最终程序。
优点:
缺点: