有朋友问道:我们平常写的代码(源文件),是如何变成最后可执行程序的呢?
这就要讲到C中的翻译环境了
在ANSI C(标准C)中规定,
test.c(源文件 也就是我们写的代码)会通过 翻译环境 变成test.exe(可执行程序)
-
程序的翻译环境
翻译环境由 编译 ,汇编 ,链接 三个部分组成
接下来详细介绍下这些过程
-
编译
编译过程 由预编译(又称 预处理)和编译 组成
-
预编译
在这过程中 主要做的就是些 “文本操作”
1.头文件的包含 #include
2.注释的删除
3.#define符号的转换
- 编译
到这一步 会将test.c源文件 转变成 (也就是生成该文件) test.s
里面全是些汇编代码
把C语言代码转换成了汇编代码 并且还会做以下工作
1.语法分析
2.词法分析
3.语义分析
4.符号汇总 <—— 在链接中会使用
-
汇编
在汇编阶段
1.把汇编指令转换成二进制指令
2.形成符号表
且生成 test.o //二进制文件 obj目标文件
-
链接
在链接阶段
1.合并段表
2.符号表的合并和重定位
由于有符号表的存在 函数可以跨文件使用
如果有报错(无法解析外部符号)那就是在链接阶段出现的
最后生成 test.exe 可执行程序
-
预处理详解
预处理符号
在C语言中 我们有各种各样的预处理符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
#define 定义标识符
语法 : #define name stuff
在预处理阶段 这些都会直接替换
在C语言中 允许把参数替换到文本中 这种实现称为 “宏”
#define 定义宏
#define MAX(x,y) (x > y ? x : y)//其实这种写法有问题 我等会说
#define 宏的替换规则 (在预处理阶段 完全替换)
这样的话 在执行速度上极大提升 但会出现一些异常情况
例如 :
#define SQUARE(x) x*x
int main()
{
printf("%d",SQUARE(5)); // 5*5
printf("%d",SQUARE(5+1)); // 5+1*5+1
return 0;
}
宏不能处理这种特殊情况 容易出bug 建议宏使用在一些简单 计算量小的工作里
我们得对宏进行修改
#define SQUARE(x) ((x)*(x))
int main()
{
printf("%d",SQUARE(5)); // 5*5
printf("%d",SQUARE(5+1)); // ((5+1)*(5+1))
return 0;
}
所以在前面的 MAX宏 你知道该怎么修改了吗 快动手试试吧
#define MAX(x,y) (x > y ? x : y)
除此之外 宏还不能出现递归 和 字符串不能被替换 ;
-
宏VS函数
宏和函数的功能类似 所以是宏更好 还是函数更好呢?
通常什么情况下我们会使用 宏
1.调用函数和从函数返回的代码时间大于计算时间
2. 宏通常用于执行简单运算
3.函数的参数 必须给定一个确定的类型 而宏是与类型无关的 整型,浮点型,长整型都ok
宏的缺点
1.每次使用宏 都会插入程序 (直接替换)多次使用会变复杂
2.不方便 调试
3.由于与类型无关 所以不够严谨
4.宏可能会带来运算符优先级的问题
5.宏无法进行递归操作
条件编译
什么是条件编译 满足条件的编译 不满足条件的 不参加编译
#ifdef #endif 这两个是一对的
#if defined(MAX) #ifdef MAX
//使用 //使用
#endif #endif 这两个等价
条件编译有什么用吗 在<stdio.h>这种头文件中常用 在跨平台的设计中常用
如何避免头文件重复包含
1.#pragma once
2. #ifndef _TEST_H
#define _TEST_H
#endif
下面来考考大家 这些预处理指令 分别都是什么意思
#pragma once
#pragma pack
#pragma comment
总结
以上就是关于 预处理和程序环境 的全部内容啦 喜欢的朋友 记得一键三连 你的点赞就是我创作的动力