C++ 是一种强类型的编程语言,支持面向对象、泛型和低级内存操作。它的工作机制包括从编写源代码到生成可执行文件的一系列步骤。C++与文件无关,文件只是容纳运行内容的载体,需要对文件以目标系统的规则编译后,才能在目标系统中运行。
预处理
在编译之前,预处理器会处理源代码中的预处理指令(例如 #include
、#define
等)。预处理器会执行以下操作:
- 文件包含:处理
#include
指令,将头文件内容插入到源文件中,可被理解是一种简单复制。
例如,在test.h中只写一个 } ,此时下面两种情况的结果是一致的,“#include”只是将目标文件的代码粘贴在当前代码中,并不会做其他任何操作。
- 宏替换:处理
#define
指令,替换宏定义,即将关键词替换为指定内容。
下图中,可以使用“INTEGER”代替“int”,编译运行正确,这是因为,在编译之前,即预处理时,“INTEGER”就已经被替换为“int”了,因此编译器根本就不知道"INTEGER"的存在,也就不会发生编译错误。
- 条件编译:处理
#if
、#ifdef
、#ifndef
等指令,控制哪些部分的代码会被编译。
“#if 0”代表禁用,即不会被编译器编译。
“#ifdef”代表如果宏定义了某个参数,则执行内部的代码,否则跳过。
“#ifndef”代表如果没有宏定义某个参数,则执行内部的代码,否则跳过。
预处理的结果是一个纯粹的 C++ 源代码文件,没有任何预处理指令。
编译
编译器将预处理后的 C++ 源代码转换为目标代码(通常是机器码)。
- 语法分析:检查代码的语法是否正确。
- 语义分析:检查代码的语义是否正确(例如类型检查)。
- 代码生成:生成目标代码,即机器码。
编译的输出通常是一个或多个目标文件( .obj 或 .o文件)。
链接
为了便于维护和程序的简洁性,需要在多个cpp文件中对用户自定义的各种函数进行定义,此时就需要使用链接器将目标文件和库文件链接在一起,生成最终的唯一的可执行文件(.exe)。链路器会检查执行程序内需要使用的外部函数,即不在本文件内被声明定义的函数。然后将他们与被执行程序链接在一起。于是,在执行程序时,如果遇到了本文件内无声明定义的函数,就可以根据链接找到对应的文件里调用执行了。
在链接时,并不是将所有cpp文件都与被执行程序链接。如果被执行程序中,没有使用这个外部函数,它就不会将这个函数链接进来。一个例外是,在被执行的程序中,如果这个函数在另一个被声明的函数体里,则即便它没有被使用,也会被链接。这是因为即便这个文件不使用这个函数,链接器不能确定其他文件里不会使用它,所以会被链接。
如下图所示,log.h中声明和定义了函数log,在主函数中将log嵌套在另一个函数printfunc中。如果不包含头文件log.h,则会报错:log未被声明。这其实很奇怪:因为我们注释了printfunc,即在主函数中不使用它,也就不使用log函数,但它依然会在链接时报错(link error),原因是不确定其他翻译单元使用printfunc函数。
如果不嵌套函数,也不包含头文件,同时不使用函数log,则不会报错。
链接的结果是一个可执行文件,可以在操作系统中运行。
运行
生成的可执行文件可以在目标操作系统上运行。运行时,操作系统会加载程序,将其分配到内存,并开始执行程序的入口点,通常是 main函数,但也可以设置成其他函数,只是需要有一个入口点,但不一定是main。