程序环境:
1、C语言的任何一种实现,存在两个不同的环境;
2、翻译环境:将源代码转换成可执行的二进制指令(机器指令);.c文件(源文件------文本信息的代码)->(翻译环境)->.exe文件(可执行文件);
2、运行环境:实现可执行文件,执行到我们想要的结果;
翻译环境:
1、编译:
定义:
依赖于编译器(vs中的编译器叫做:cl.exe),编译器只有一个;源文件通过编译器编译生成目标文件(目标文件已经是二进制的了);
1、预编译(预处理):
进行文本操作的处理(增、删、改),还是我们所写的C语言代码,生成.i文件;
- 将代码中的注释替换成了空格;
- 头文件的包含;
- #开头的都是预处理指令,所有的预处理指令都是在预处理阶段处理的;
2、编译:
把C语言代码翻译成了汇编代码,生成.s文件;
- 词法分析;语法分析;语义分析;符号汇总(专业术语);
3、汇编:
将汇编代码翻译成二进制指令,生成目标文件;
- 生成符号表;
2、链接:
定义:
依赖于连接器(vs中的链接器叫做:link.exe),链接库和目标文件通过连接器形成可执行文件/程序;多个目标文件可以一起链接;
目标文件:windows环境下的目标文件后缀是:.obj;Linux环境下生成的目标文件后缀是:.o;
链接:
链接目标文件和链接库生成可执行程序(二进制的程序);
- 合并段表;
- 符号表的合并和重定位;
运行环境:
1、程序必须载入到内存中,一般在有操作系统的环境下完成;
2、找到main函数开始运行;
3、开始执行程序代码,使用一个运行时堆栈(函数栈帧空间),存储函数的局部变量和返回地址。程序还可以使用静态内存、存储在静态内存中的变量,在程序执行过程中一直保留这些静态变量的值;
4、终止程序,结束main函数的运行;
图示编译链接:
书本推荐:
程序员的自我修养
预处理
预定义符号:
FILE // 当前编译的文件
LINE// 当前编译的行号
DATE// 当前编译的日期
TIME// 当前编译的时间
FUNCTION// 当前编译的函数
#define定义标识符
1、定义的标识符只会替换,不会进行计算;
2、#define定义的标识符在预处理阶段,这些标识符将会被替换;
3、#dedine定义的内容可以是多种多样的;
4、#define定义符号的最后不加分号,因为在替换的时候,如果加了分号后会把分号也替换进去;
#define定义宏
1、允许把参数替换到文本中;
2、看代码会更容易理解;
// #define定义宏
#define a(n) (4 + 2)*(n)
// 参数括号必须紧挨a,如果加了空格隔开,将会当做替换部分;
// 需要将括号加上;
int main()
{
int z = 0;
z = 2 * (2 + a(4 + 1)); // a(4 + 1) -> a(5)
// 2 * (2 + 6 * 5) = 64
return 0;
}
#define替换规则
1、在调用宏时,先对参数进行检查,包含任何由#define定义的符号则首先被替换。
2、将宏定义的替换文本插入到原来文本的位置;
3、宏参数和#define定义中可以出现其他#define定义的变量;
4、宏不能出现递归;
# 和 ## 的作用
和 ## 都是在宏内实现的;
1、#把一个宏参数转化为对应的字符串;
2、##可以将位于##两端的符号合成一个符号;
#代码:
#include <stdio.h>
#define PRINT_1(value, format) printf("相加的值为:"format"\n", value)
#define PRINT_2(value, format) printf(""#value"相加的值为:"format"\n", value)
// #可以将宏的参数转化为对应的字符串;
int main()
{
int a = 10;
int b = 20;
PRINT_1(a + b, "%d");
PRINT_2(a + b, "%d");
return 0;
}