本章介绍处理源代码的第一步:预处理
只所以说是处理源代码的第一步,而不是编译的第一步,是因为准确来讲,预处理是发生在编译之前的事情,不属于微观意义上编译的过程。
那么下面就来探讨一下,预处理到底发生了什么。
假设有如文件tmp.c中下一段代码
#include <stdio.h>
#include <stdlib.h>
#define TMP 3
int main()
{
int tmp = TMP;
printf("tmp = %d \n", tmp);
return;
}
然后对这个文件执行gcc -E tmp.c -o tmp.i (gcc的-E选项为只进行预处理操作)
生成的tmp.i即为tmp.c经过预处理之后的的文件,ll看一下,发现这个文件要比c文件大很多
-rw-rw-r-- 1 yejing yejing 125 Oct 16 07:17 tmp.c
-rw-rw-r-- 1 yejing yejing 41648 Oct 16 07:17 tmp.i
那么到底具体多了什么呢?
截一段出来看看
# 1 "tmp.c"
# 1 "<command-line>" //如果有人知道,烦请指导一下,这一行是什么意思?
# 1 "tmp.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 28 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 324 "/usr/include/features.h" 3 4
# 1 "/usr/include/i386-linux-gnu/bits/predefs.h" 1 3 4
# 325 "/usr/include/features.h" 2 3 4
# 357 "/usr/include/features.h" 3 4
# 1 "/usr/include/i386-linux-gnu/sys/cdefs.h" 1 3 4
# 378 "/usr/include/i386-linux-gnu/sys/cdefs.h" 3 4
# 1 "/usr/include/i386-linux-gnu/bits/wordsize.h" 1 3 4
# 379 "/usr/include/i386-linux-gnu/sys/cdefs.h" 2 3 4
# 358 "/usr/include/features.h" 2 3 4
# 389 "/usr/include/features.h" 3 4
# 1 "/usr/include/i386-linux-gnu/gnu/stubs.h" 1 3 4
gcc官方网站是这么解释这个这个格式的(https://gcc.gnu.org/onlinedocs/gcc-4.3.6/cpp/Preprocessor-Output.html)
# linenum filename flags
These are called linemarkers. They are inserted as needed into the output (but never within a string or character constant).
They mean that the following line originated in file filename at line linenum.
filename will never contain any non-printing characters; they are replaced with octal escape sequences.
//文件名不包含无法打印字符,他们将被八进制转义字符代替
对后面的标志的具体解释如下:
After the file name comes zero or more flags, which are `1', `2', `3', or `4'. If there are multiple flags, spaces separate them. Here is what the flags mean:
//文件名之后是0到多个标志,有可能是1,2,3,4,如果有多个标志,用空格分隔,下面是各个标志的具体意思
`1'
This indicates the start of a new file. //1表示这是个以新行
`2'
This indicates returning to a file (after having included another file). //2表示要返回一个文件(表include的文件include了另外一个文件)
`3'
This indicates that the following text comes from a system header file, so certain warnings should be suppressed.
//表示下面的代码来自一个系统头文件,需要控制警告
`4'
This indicates that the following text should be treated as being wrapped in an implicit extern "C" block.
//表示以下代码应该被当做类似被extern“C”包含的代码块来处理
前面的文章说了,宏观意义上的编译实际上包括了预处理,编译,汇编,连接等流程,所以我们常用的的gcc实际上也不是一个程序,而是多个程序组成的package,
其中包括预处理器cpp,编译器ccl(高版本的gcc已经将预处理合到了ccl中),汇编器ar,链接器ld等。
实际上完成上述gcc -E动作的就是cpp,
cpp作为一个单独的应用程序,有些常用的选项参数,比如-I和-D,
-I可以用于处理头文件的包含,-I包含的头文件,可以直接include,而不用使用相对路径
-D可以用于灵活的打开或者关闭用-D之后的宏控制的代码块
上面是举例说明了预处理大概做了些什么,下面介绍下,预处理主要处理源文件中的哪些部分。
总体来说,主要是以“#”开头的行。
1,#define、#undef:宏的定义及却小
2,#include:头文件的包含,
3,#line num:#line所在行的下一行行号为num
#include <stdio.h>
#line 30
int main(){
printf("%d \n", __LINE__);
return 1;
}
程序输出32
4,#error text:如果执行到带#error的行,编译会推出,并输出后面的text给用户,make中也有类似的函数
5,#program:(http://blog.youkuaiyun.com/welcome_ck/article/details/232434),这个命令比较少用,之前这个blog做了比较详细的介绍。
6,#if、#ifdef、#ifndef、#elif、#else、#endif:代码块编译控制