一.代码运行是的两种环境
1.翻译环境,在这个环境中源代码被转换为可执行的机器指令。
2.执行环境,它用于实际执行代码
下面主要讲解翻译环境。
二.翻译环境
从.c 文件到 .exe 文件需要经过编译器的翻译,而翻译又分为 编译和链接两个部分
编译又分为三个部分:
1.预编译:又叫预处理,在这个部分主要完成头文件的包含,#define的替换,注释的删除;
2.编译:主要完成语法分析,词法分析,词义分析,符号汇总(符号包括全局性的变量和函数),生成汇编代码;
3.汇编:生成二进制指令,形成符号表(符号表是由符号和其地址组成的);
链接:合并段表,合并符号表(在这个阶段会发现未定义的函数)。
见下图:
三.预定义符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
四.#define
1.define 定义宏
宏的申明方式:
#define name( parament-list ) stuff
其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。
注意 name 需与后面的括号紧密相连,不可以有空格,如果有任何空白存在,参数列表就会被解释为stuff的一部分。
注意当我们定义宏的时候,不要吝啬括号!
来看下面一个例子:
1. #define MOD(x,y) x*y 2. 3. int main() 4. { 5. int m = MOD(2+3,2); 6. printf("%d\n", m); 7. return 0; 8. }
对初学者来说,这段代码的答案很容易被认为式10,但事实并非如此,因为宏是在预处理阶段先替换掉,然后在进行计算,所以在没有括号的情况下,替换后是这样的:2+3*2=8;所以若是想要得到10这个结果,就要加上括号,即:
#define MOD(x,y) ((x)*(y))
2.带有副作用的宏参数
我们知像是前置++ ,后置++这种的运算符是会改变操作数的值属性的,那它如果应用到#define 定义的宏中会是怎么样呢?
我们来看下面这个例子:
1. #define MAX(x,y) ((x)>(y)?(x):(y)) 2. 3. int main() 4. { 5. int a = 4; 6. int b = 6; 7. int m = MAX(a++, b++); 8. printf("m=%d\n", m); 9. printf("a=%d b=%d\n", a, b); 10. return 0; 11. }
最后的答案会是多少呢?
首先完成宏参数的替换:((a++)>(b++)?(a++):(b++))
后置++是先使用后++,因为4<6,所以执行后面的 b++,经过前面的++,此时a=5,b=7,所以先把7赋给m,然后b++,得到b=8;
即m=7 a=5 b=8
总结:
1.#define 定义的符号需要先原封不动的替换掉,所以建议在#define 后面不加 ' ; ' ;
2.#define 定义的宏不要吝啬括号,以免出现出乎意料的结果;
3.避免使用带有副作用的运算符。
五.#define定义宏 与函数对比
六.预处理指令
所有的预处理指令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理指令应从第一列开始。下面列出了所有重要的预处理指令: