gcc编译过程详解与ACM时间和进度条的制作
文章目录
1. 编译详细过程与ACM时间
我们知道编译分为四大步:预处理、编译、汇编、链接,但是详细的情况并不像我们想的那样,因为编译器会做很多事情,比如:宏替换、头文件展开、语法检查、语义检查、代码优化、生成汇编代码、生成可执行文件等。
1.1 预处理
gcc -E main.c -o main.i
解析:
- gcc:编译器
- -E:将源代码文件进行预编译处理,形成.i文件
- main.c:源代码文件
- -o:指定目标文件的名称或者存放路径
- main.i:预处理后的目标文件
预处理主要完成以下工作:
- 头文件展开
- 处理所有#include预处理指令
- 将头文件的内容复制到当前文件
- 可以递归展开(头文件中包含的头文件)
- 宏定义替换
- 展开所有#define宏定义
- 处理条件编译指令(#if、#ifdef、#ifndef等)
- 展开所有宏调用
#define Max(a, b) ((a) > (b) ? (a) : (b))
int max = Max(3, 4); //展开为:int max = ((3) > (4) ? (3) : (4))
-
条件编译处理
- #if、#ifdef、#ifndef:条件判断
- #elif、#else:分支处理
- #endif:结束条件编译
#ifdef DEBUG printf("debug info\n"); #endif
-
删除注释
- 删除所有//和/* */格式的注释
- 每个注释都替换为一个空格
-
添加行号和文件名标识
- 使用#line指令标记行号和文件名
- 用于编译器产生调试信息和编译错误提示
-
处理特殊预处理指令
- #pragma:编译器指令
- #error:产生编译错误
- #warning:产生编译警告
-
保留所有的换行符
- 确保错误提示的行号正确
- 方便调试
-
字符串常量化
- 处理#运算符,将宏参数转换为字符串
#define STR(s) #s STR(hello) // 展开为: "hello"
-
宏连接操作
- 处理##运算符,连接两个记号
#define CONCAT(a,b) a##b CONCAT(x,y) // 展开为: xy
示例:
// 源文件 main.c
#include <stdio.h>
#define MAX 100
#define SQUARE(x) ((x)*(x))
int main() {
int value = SQUARE(MAX);
return 0;
}
// 预处理后 main.i(简化版)
// ... stdio.h的内容 ...
int main() {
int value = ((100)*(100)