目录
1.翻译环境和运行环境
在ANSI C的任何⼀种实现中,存在两个不同的环境
第一种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。
第二种是执行环境,它用于实际执行代码。
2.翻译环境
翻译环境由编译和链接两大内容组成,其中编译又依靠预编译、编译、汇编三个过程
2.1预处理
(1)预定义符号
__FILE__
: 它表示当前源文件的名称。__LINE__
: 它表示当前源文件的行号。__DATE__
: 它表示源文件被编译的日期。__TIME__
: 它表示源文件被编译的时间。__STDC__
: 如果编译器遵循ANSI C标准,那么它的值就是1。__FUNCTION__
: 它表示当前函数的名称。
#include <stdio.h>
int main() {
printf("File: %s\n", __FILE__);
printf("Line: %d\n", __LINE__);
printf("Date: %s\n", __DATE__);
printf("Time: %s\n", __TIME__);
#ifdef __STDC__
printf("This is a ANSI C compiler\n");
#else
printf("This is not a ANSI C compiler\n");
#endif
printf("Function: %s\n", __FUNCTION__);
return 0;
}
这段代码会打印出当前源文件的名称、行号、编译日期、编译时间、是否为ANSI C编译器,以及当前所在的函数。
注意:__FUNCTION__
是C99标准引入的,如果你的编译器不支持C99,那么你将无法使用这个预定义符号。
(2)define定义常量
语法:#define name stuff
#define MAX 100
#define reg register
注:语句后不加;(分号)
(3)define定义宏
#define机制包括一个规定,允许把参数替换到文本中,通过这种实现统称为宏或宏定义
语法
#define name(parament_list) stuff
注:parament_list为参数列表,参数列表左括号必须与name紧邻
宏参数是完全替换,用于求值时可能会出现运算等级问题,所以宏定义要多加括
例如:
#define DOUBLE(x) ((x)+(x))
带有副作用的宏参数尽量不用:x++
(4)宏规则及其特点
- #define定义中可以出现其他#define定义的符号
- 宏中不能出现递归
- 宏的参数是无关类型的
- 执行速度快
- 没法调试
- 容易出现操作符优先问题
所以进行一些简单操作运算时可以使用宏
(5)操作符#和##
- #
#将宏的一个参数转换成字符串字面量(字符串化),仅允许出现在宏替换列表
#define PRINT(n) printf("the value of"#n"is"%d",n)
//预处理为:
printf("the value of a is "%d",a)
2.##
## 可以把位于两边的符号生成一个符号,连接必须产生一个合法标识符,否则是未定义的
(6)命名约定
宏全大写,函数名不全都大写
(7)#undef
用于移除一个宏定义
#undef name
(8)条件编译
在编译一个程序的时候将一条语句 (一组语句) 编译或放弃。
当满足条件时再继续编译
-
#if //常量表达式 为真存在,为假注释掉 //... #endif
注:不能为变量,因为变量在程序运行时才产生,此为翻译条件
- 多分支
#if 常量表达式 //... #elif 常量表达式 //... #else //... #endif
- 判断是否被定义
#if defined(symbol) #ifdef symbol #if !defined(symbol) #ifndef symbol
- 嵌套指令
2.2编译
编译过程就是将预处理后的⽂件进⾏⼀系列的:词法分析、语法分析、语义分析及优化,⽣成相应的 汇编代码⽂件。
编译后,C语言代码被翻译成了汇编代码。
汇编将汇编指令译成了二进制的指令。
2.3汇编
汇编器是将汇编代码转转变成机器可执⾏的指令,每⼀个汇编语句⼏乎都对应⼀条机器指令。就是根 据汇编指令和机器指令的对照表⼀⼀的进⾏翻译,也不做指令优化。
3.链接
链接是⼀个复杂的过程,链接的时候需要把⼀堆⽂件链接在⼀起才⽣成可执⾏程序。 链接过程主要包括:地址和空间分配,符号决议和重定位等这些步骤。 链接解决的是⼀个项⽬中多⽂件、多模块之间互相调⽤的问题。
每个源⽂件都是单独经过编译器处理⽣成对应的⽬标⽂件。
test.c 经过编译器处理⽣成test.o
add.c 经过编译器处理⽣成add.o
4.运行环境
1. 程序必须载⼊内存中。在有操作系统的环境中:⼀般这个由操作系统完成。在独⽴的环境中,程序 的载⼊必须由手工安排,也可能是通过可执行代码置⼊只读内存来完成。
2. 程序的执行便开始。接着便调用main函数。
3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程 ⼀直保留他们的值。
4. 终止程序。正常终止main函数;也有可能是意外终止。