1.翻译环境和执行环境
在 ANSIC标准中,程序实现存在两个不同的环境
翻译环境,源代码被转换为可执行的机器指令 执行环境,用于实际执行代码
2.编译+链接
2.1翻译环境
1.组成一个程序的每个源文件通过编译过程分别转化为目标代码 2.每个目标文件由链接器捆绑在一起,形成单一且完整的可执行程序 3.链接器同时也会引入标准C函数库中任何被该程序所用到的函数
2.2编译
2.3运行环境
程序执行过程
程序必须载入内存中,在由操作系统的环境中:一般由操作系统完成,在独立的环境中,程序的载入需由手工安排,也可能通过可执行代码置入只读内存来完成
程序的执行开始后,接着调用mian函数
开始执行程序代码,程序将会使用一个运行堆栈,存储函数的局部变量和返回地址。程序同时也可以使用静态内存,存储于静态内存中的变量在程序的整个执行过程一直保留其值
终止程序,正常终止main函数,也存在意外终止
3.预处理
3.1预定义符号
__FILE__ 进行编译的源文件 __LINE__ 文件当前的行号 __DATE__ 文件被编译的日期 __TIME__ 文件被编译的时间 __STDC__ 如果编译器遵循ANSIC标准,其值为 1
实例
int main() { printf("%s\n", __FILE__); return 0; }
3.2#define
3.2.1#define定义标识符
#define name stuff
实例
#define ROW 10 #define COL 10 #define reg register 为register,创建一个简短的名字
在define定义标识符时,在最后不要加上;,主要思想是替换
3.2.2#define定义宏
#define机制包括一个规定,允许把参数替换到文本中, 这种实现通常称为宏或者定义宏
宏的申明方式:
#define name(parament-list)stuff
parament-list是由逗号隔开的符号表
参数列表的左括号必须与name紧邻
如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分
实例
#include<stdio.h> #define SQUARE(x) x*x int main() { printf("%d\n", SQUARE(5)); return 0; }
提示
用于对数值表达式进行求值的宏定义都应该用加上括号这种方式, 避免在使用宏时由于参数中的操作或临近操作符之间不可预料的相互作用
3.2.3#define替换规则
在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果包含,则首先被替换
替换文本随后被插入到程序中原来文本的位置。宏,参数名被其值所替换
最后,再次对结果文本进行扫描,观察是否包含任何由#define定义的符号。如果包含,则重复上述操作
1.宏参数和#define定义中可以出现其他#define定义的符号,但是对于宏,不能出现递归
2.当预处理器搜索#define定义的符号时,字符串常量的内容不被搜索
3.2.4#和##
int main() { printf("hello ""crush"); return 0; }
从上面的代码可以发现,字符串有自动连接的特点
如果字符串中存在宏参数该如何自动链接呢
#,可以把宏参数变成对应的字符串
代码实现如下
#include<stdio.h> #define PRINT(N) printf("the value of "#N" is %d\n",N) int main() { int i = 5; PRINT(i); printf("the value of ""i"" is %d\n", i); return 0; }
由运行结果可以说明,#的作用:把一个宏参数变成对应的字符串
##的作用
##可以把位于其两边的符号合成一个符号 允许宏定义从分离的文本片段创建标识符
代码实现
#include<stdio.h> #define SUM(hello,crush) hello##crush int main() { int hellocrush = 123; printf("%d\n", SUM(hello, crush)); return 0; }
3.2.5含有副作用的宏参数
当宏参数在宏定义中出现超过一次时,如果参数带有副作用,在使用宏时就会出现问题,导致不可预测的后果。副作用就是表示式求值时出现的永久性效果。
例如
x+1 没有副作用 x++ 带有副作用
观察下列代码及运行结果
#define MAX(a,b) ((a>b)?(a):(b)) int main() { int a = 2; int b = 3; int c = MAX(a++, b++); //c=((a++>b++)?(a++):(b++)) printf("a=%d b=%d c=%d\n", a, b, c); return 0; }
后置加加带有的副作用,使运行结果出乎意料,所以应尽量避免使用带有副作用的宏参数
3.2.6宏和函数
宏通常被应用于执行简单的运算
宏与函数相比
优点
用于调用函数和从函数返回的代码可能比实际执行程序需要的时间更多,所以宏比函数在程序的规模和速度方面更胜一筹
函数的参数必须声明为特定的类型
故函数只能在类型合适的表达式上使用,相反宏可以适用任何类型的数据进行比较
宏的类型是任意的
缺点
每次使用宏时,都需要将宏定义的代码插入程序中,如果宏比较长,可能大幅度增加程序的长度反而降低程序的可读性
宏不能进行调试,因为宏是被替换的
宏由于类型任意,也就不够严谨
宏可能会带来运算符优先级的问题,导致程序出现错误
3.2.7命名约定
一般情况下,函数和宏的使用语法类似,故语言本身没办法区分二者
所以有一个习惯
宏名全部大写 函数名不全部大写