1、C语言的任何一种实现,存在两个不同的环境;
2、翻译环境:将源代码转换成可执行的二进制指令(机器指令);.c文件(源文件——文本信息的代码)->(翻译环境)->.exe文件(可执行文件);
2、运行环境:实现可执行文件,执行到我们想要的结果;
1、编译:
定义:
依赖于编译器(vs中的编译器叫做:cl.exe),编译器只有一个;源文件通过编译器编译生成目标文件(目标文件已经是二进制的了);
进行文本操作的处理(增、删、改),还是我们所写的C语言代码,生成.i文件;
把C语言代码翻译成了汇编代码,生成.s文件;
将汇编代码翻译成二进制指令,生成目标文件;
2、链接:
定义:
依赖于连接器(vs中的链接器叫做:link.exe),链接库和目标文件通过连接器形成可执行文件/程序;多个目标文件可以一起链接;
目标文件:windows环境下的目标文件后缀是:.obj;Linux环境下生成的目标文件后缀是:.o;
链接目标文件和链接库生成可执行程序(二进制的程序);
1、程序必须载入到内存中,一般在有操作系统的环境下完成;
2、找到main函数开始运行;
3、开始执行程序代码,使用一个运行时堆栈(函数栈帧空间),存储函数的局部变量和返回地址。程序还可以使用静态内存、存储在静态内存中的变量,在程序执行过程中一直保留这些静态变量的值;
4、终止程序,结束main函数的运行;

程序员的自我修养
__FILE__ // 当前编译的文件
__LINE__// 当前编译的行号
__DATE__// 当前编译的日期
__TIME__// 当前编译的时间
__FUNCTION__// 当前编译的函数
1、定义的标识符只会替换,不会进行计算;
2、#define定义的标识符在预处理阶段,这些标识符将会被替换;
3、#dedine定义的内容可以是多种多样的;
4、#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;
- }
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;
- }
| 宏 | 函数 |
| 用于执行简单运算,宏的效率高(仅是完成替换) | 用于执行简单运算,函数相对宏的效率低(涉及函数调用、参数传递、栈帧的创建、计算、函数的返回) |
| 宏的参数类型可以是任意类型 | 函数有固定的参数类型 |
| 宏定义很长,插入程序中就会增加程序长度 | 函数很长并不会增加程序长度,只需要调用函数即可 |
| 宏不方便调试,不可递归 | 函数便于调试,可以递归 |
| 宏的参数是替换,会有运算符优先级问题,所以要多加括号 | 函数参数是调用的时候计算的 |
总结:逻辑比较简单的使用宏,计算逻辑比较复杂的使用函数;
#if 常量表达式 // 如果满足常量表达式,则执行下面编译
// 编译内容
#endif // 结束条件编译
#if 常量表达式 // 满足常量表达式,执行以下内容
#elif 常量表达式 // 满足常量表达式,执行以下内容
#else // 上面#elif常量表达式不满足,就执行这句以下的
#endif // 结束条件编译
#if defined (宏名)
// 内容 // 如果执行则定义了此宏
#endif // 结束判断
#ifdef 宏名
// 内容 // 执行则定义了此宏
#endif // 结束判断
#if !defined (宏名)
// 内容 // 如果执行,则表明未定义此宏
#endif // 结束判断
#ifndef 宏名
// 内容 // 如果执行,则表明未定义此宏
#endif // 结束判断
1、本地文件就是自己创建的.h文件,使用的是双引号引起来;
2、库文件就是标准库中的、第三方的,使用尖括号进行括起来;
3、使用双引号,编译器找头文件,是先找源文件目录,未找到的则去库函数的头文件里找,再没找到就报错;
4、使用尖括号,编译器找头文件,查找头文件去标准路径下查找,未找到就报错;