C/C++编译问题

预处理

预处理我们接触最多的就是包含头文件的 #include 和定义宏的 #define,同时还有规范性的头文件保护宏。在这个环节出现最多的问题是找不到头文件,然后编译器停止工作。这个问题是最容易被解决的,但是很多人可能并不是很了解编译器寻找头文件的流程。这里我们首先要区分 3 种头文件,第一种是标准库的头文件,第二种是代码中引用到的第三方库的头文件,第三种是自己代码编写的头文件。编译器在工作时是有一个头文件目录列表的,根据目录列表去寻找头文件,第一个目录便是当前代码的所在的目录,其次是编译器自行定义的目录(一般是标准库头文件所在的目录),最后是我们自己在编译时加上的目录列表,可以有多个,包括需要引用的第三方库的头文件目录和自己代码的头文件目录(可能你自己写的头文件跟源文件不在一个目录下)。

编译与汇编

编译环节出错那基本就是语法的问题,代码写得有问题直接导致编译器停止工作。这里不得不提一下 C++11 标准,并不是所有编译器都实现了新标准的所有规范,同时可能编译器之间实现的特性还有差异,这些在写跨编译器或跨平台代码事要特别注意,同一个编译器的不同版本之间也会略有差异。

链接

链接环节出现问题非常之多,类型也是五花八门,奇奇怪怪,会非常坑。但是归根结底主要是两种,第一是链接时找不到符号,第二是链接时找到了多个符号。很多同学碰到链接出错时编译器吐出来的一堆一堆乱七八糟的函数符号估计都很蛋疼,但是我们多数时候碰到的都是 undefined reference to xxx,即找不到符号。

符号

要搞明白链接时编译器是怎么工作的,我们就得先搞清楚 符号 在编译系统中的作用。一个符号可以指代一块内存或者一段代码。代码中与符号相关的几处地方如下。

变量的声明,告诉编译器有这么一个变量指代一块内存。
变量的定义,告诉编译器需要为这个变量分配一块内存。
函数的声明,告诉编译器有这么一段代码可以使用,输入输出规范如何,应该怎么调用。
函数的定义,告诉编译器这段代码的逻辑实现。
引用变量或函数,代码中使用某个变量或者调用某个函数。