
编译 C 程序这里我们所说的 C 语言程序的编译过程指的是从 C 语言源代码生成可执行程序或库的过程。通常该过程要经过四个阶段包括C 语言程序编译过程示例程序举例来说我们在名为 main.c 的源文件中输入下面一段简单代码它在 main 函数中立即返回返回值是用宏定义的 0 值。/* 演示 C 语言程序的编译过程 */ #define SUCCESS 0 int main() { return SUCCESS; }如果使用 gcc 编译器我们可以逐步观察这个示例程序的编译过程。预处理gcc 用 -E 选项让编译器在预处理阶段结束后立即停止不再往下执行编译。预处理过程对翻译单元中的代码进行词法和语义分析其输出是程序在目标机器上的中间表示。$ gcc -E main.c -o main.i# 1 main.c # 1 # 1 # 1 /usr/include/stdc-predef.h 1 3 4 # 1 2 # 1 main.c int main() { return 0; }预处理结束后注释被替换成了空白行宏定义被替换成了实际值。如果有 #include 预处理指令该指令后面的头文件会被包含进中间文件。编译gcc 的 -S 选项让编译器在编译阶段结束后立即停止不再往下执行汇编。编译阶段将预处理阶段生成的中间表示翻译为目标机器上的汇编代码。$ gcc -S main.i -o main.s.file main.c .text .globl main .type main, function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609 .section .note.GNU-stack,,progbits这里汇编代码使用的是 ATT 格式我们看到寄存器 EAX 中被写入了 0 值这是因为 main 函数最后的 ret 指令返回时会将寄存器 EAX 中的值作为返回值。汇编gcc 的 -c 选项在编译或汇编之后立即停止不再往下执行链接。$ gcc -c main.s -o main.o $ file main.o main.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped这里gcc 对汇编代码执行汇编生成了名为 main.o 的目标文件。由 file 命令可知该文件是一个 ELF 格式的可重定位文件下一步将对这个目标文件执行链接。链接不加任何选项时gcc 对目标文件执行链接生成可执行文件。$ gcc main.o -o m如以上步骤一样我们用 -o 指定输出文件名称这里是 m。该文件是一个可执行文件尽管它不输出任何内容。最后需要注意的是以上步骤只是为了演示 C 语言程序的编译过程。实际上如果要从示例中的 main.c 生成可执行文件 m我们只需要一条命令即可。$ gcc main.c -o m2之所以可以这样写是因为在正常情况下对 C 源文件调用 gcc 会执行以上所有预处理、编译、汇编以及链接过程直接生成可执行文件。#头条创作挑战赛##C语言每日小知识#