最近重新开始回顾 C 语言以及其编译后的文件格式 ELF。 暂时告别一步到位的命令 gcc main.c,如果从 .c 文件的编译来说,主要分为预编译(preprocess)、编译(Compilation)、汇编(Assembly)、链接(Linking) 四个步骤。 但是,仅仅从第一个流程 预编译 而言,就已经遇到了一些麻烦。

program.i

# 1 "program.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "program.c"
# 1 "header.h" 1
char *test(void);
# 2 "program.c" 2

int main(void)
{
}

预编译后的问题出现了诸如 # 1 "program.c"注释?

这里简单记录预处理输出文件的基本格式,方便今后回顾。

Output File format

首先,从预编译的结果看,cpp (C preprocessor) 程序主要是处理了所有的宏指令。然后添加上了一些所谓线性标记的内容。 最终得到的就是类似 program.i 的结果。

从细节上来说: 首先,所有的宏指令,包括 #include (用于引入用户自定义及系统预定义的头文件)、#define (用于将使用到的宏进行替换)。 当然,在替换完成后,这些指令所在的行将被替换成空行。同时,所有的注释行也将被替换成空行。

此外,将添加上注入 #1 "program.c"线性标记

线性标记

线性标记的标准格式:

# linenum filename flags

linenum 是为了配合预定义宏 __LINE__ 是使用的,用于定位紧随的下一行内容在原文件中所在的行。

filename 指出了接下来的内容来自哪个原文件

flags 有如下几个取值:

  • 1 : 表示这是一个新的文件的开始
  • 2 : 表示回到文件 filename 的内容 (从其他的 include 的文件中)
  • 3 : 表示接下来的内容是从系统预定义头文件中来的,因此需要抑制某些警告
  • 4 : 表示接下来的内容需要被视作是被封装的隐式 extern "C"

实例解读

program.c

#include "header.h"
int main(void)
{
    puts(test());
}

header.h

char *test(void);

接下来的演示都将以 program.cheader.h 两个文件作为标准示例。 期间,将对 program.c or header.h 做不同程度的修改,已达到更好的展示效果。 注意: 额外添加的注释由 ! 开始到该行结束(并不符合 C 语言语法),但是帮助理解

Sample 1

直接利用 program.c 与 header.h 进行预编译 cpp -o program.i program.c 结果如下:

# 1 "program.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "program.c"
# 1 "header.h" 1            ! 下一行代码对应的是 header.h 文件第一行
char *test(void);
# 2 "program.c" 2           ! flag=2 表示下列内容由回到了 program.c 中,下一行对应原文件第二行

int main(void)
{
    puts(test());
}

Sample 2

接着,给 program.c 加点注释,在加点空行

program.c - sub 1

// This is comment          ! 这里添加了一行注释
#include "header.h"

                            ! 这里加了个空行
int main(void)
{
    puts(test());
}

再看看效果

# 1 "program.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "program.c"             ! 下一行代码对应原代码中第 1 行,本来是注释,但会被预编译器处理成空行 (多行将逐一处理,但行数不会变)

# 1 "header.h" 1            ! 下面将描述 header.h 的代码
char *test(void);
# 3 "program.c" 2           ! 继续描述 program.c ,下一行对应原代码第 3 行。至于第 2 行,就是 #include "header.h" ,不会直接表现了


int main(void)
{
    puts(test());
}

Sample 3

在改变一些

program.c - sub 2

// This is comment
#include "header.h"
#define TEN 10

int main(void)
{
    puts(test());
}

看看结果

# 1 "program.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "program.c"

# 1 "header.h" 1
char *test(void);
# 3 "program.c" 2
                        ! 原代码中对应行是宏 #define TEN 10 ,已经被空行替换掉了。

int main(void)
{
    puts(test());
}
  __                    __                  
 / _| __ _ _ __   __ _ / _| ___ _ __   __ _ 
| |_ / _` | '_ \ / _` | |_ / _ \ '_ \ / _` |
|  _| (_| | | | | (_| |  _|  __/ | | | (_| |
|_|  \__,_|_| |_|\__, |_|  \___|_| |_|\__, |
                 |___/                |___/