程序的翻译环境和执行环境
第一种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。第二种是执行环境,用于实际执行代码。
翻译环境
1`组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
2`每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
3`链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也连接到程序中。
运行环境
程序执行的过程:
- 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
- 程序的执行便开始。接着便调用main函数。
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
- 终止程序。正常终止main函数;也有可能是意外终止。
预处理详解
预定义符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
这些预定义符号都是语言内置的。
#define
#define 定义标识符
#define fun function //为function这个关键字取一个简短的名字
#define 定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(definemacro)。
例如#define SQUARE(x) x*x
这个宏接受一个参数x,若x值为5,则宏的值为5×5,注意是5×5而不是25,此处涉及符号优先级问题,在程序中要具体分析,若想避免这个情况只需要在宏定义时,给宏定义表达式整体加上圆括号即可。
#define 的替换规则
1.在调用宏时,首先对参数进行检查,看看是否包含由#define定义的符号。如果是,它们首先被替换。
2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。注意:
1.宏参数和#define定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
宏和函数对比
宏通常被用于执行简单的运算,例如在两个数中找出较大的一个
#define MAX(a,b) ((a)>(b)?(a):(b))
那为什么不用函数来完成这个任务?
原因有两个:
1.用于调用函数和从函数返回代码可能比实际执行这个小型计算工作所需要的时间更多,所以宏
比函数在程序的规模和速度方面更胜一筹。
2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的。
当然和宏相比函数也有劣势的地方:
- 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
- 宏是没法调试的。
- 宏由于类型无关,也就不够严谨。
- 宏可能会带来运算符优先级的问题,导致程容易出现错。
宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。
宏和函数的对比
属性 | #define定义的宏 | 函数 |
---|---|---|
代码长度 | 每次使用时,宏代码都会被插入到程序中,除了非常小的宏之外,程序的长度会大幅度增长 | 函数的代码只出现于一个地方,每次使用这个函数时,都调用那个地方的同一份代码 |
执行速度 | 更快 | 存在函数调用和返回的额外开销,所以相对慢一些 |
操作符优先级 | 宏参数的求值是在所有范围表达式的上下文环境里,除非加上括号,否则临近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多加括号 | 函数参数只在函数调用的时候求值一次,它的结果值传给函数,表达式的求值结果更容易预测 |
带有副作用的参数 | 参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果 | 函数参数只在传参的时候求值一次,结果更容易控制 |
参数类型 | 宏的参数与类型无关,只要对参数的操作合法,它就可以使用任何参数类型 | 函数的参数类型与类型有关,如果参数的类型不同,就需要不同的函数,即使它们的执行的任务是相同的 |
调试 | 宏不方便调试,因为在预编译时就完成了替换 | 函数可以逐语句调试 |
递归 | 宏不能递归 | 函数可以递归 |