目录
1.程序的翻译和执行环境
程序由test.c到输出结果需要经过两个环境:翻译环境和执行环境
2.编译+链接
2.1 翻译环境
2.2 编译的几个阶段
2.3 运行环境
程序执行的过程
1.程序载入在内存中
2.调用main函数
3.开始执行代码
4.终止程序
3.预处理详解
3.1 预定义符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__TIME__ //文件被编译的时间
__func__ //所处的函数
printf("FILE:%s fun:%s LINE:%d TIME:%s", __FILE__, __func__, __LINE__, __TIME__);
运行结果
3.2 #define
3.2.1 #define定义标识符
语法
#define name stuff
例如
#define MAX 100
注意:#define定义标识符时,最后不需加上;(分号)
3.2.2 #define 定义宏
#define name( parament-list ) stuff
如:
#define SQUARE(x) x*x
需注意的是宏把SQUARE中的x替换到了x*x中 ,这样一来会产生问题
如
#define SQUARE(x) x*x
int main()
{
int a = 5;
printf("%d\n", SQUARE(a + 1));
return 0;
}
输出的结果为11而并非25,这是因为程序直接把a+1带到宏中,输出的是5+1*5+1的结果11
注意:
对于数值表达式进行求值的宏定义都应该加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间产生不可预料的作用
3.2.3 带副作用的宏参数
x+1;//不带副作用
x++;//不带副作用
#define MAX(a,b) ((a) > (b) ? (a):(b))
int main()
{
int x = 5;
int y = 8;
int z = MAX(x++, y++);
printf("%d\n", x);
printf("%d\n", y);
printf("%d\n", z);
return 0;
}
以上代码输出的结果为6 10 9
造成的原因时由于具有副作用的参数所引起的问题
3.2.4 宏和函数比较
属性 | #define定义宏 | 函数 |
代 码 长 度 | 每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长 | 函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码 |
执 行 速 度 | 更快 | 存在函数的调用和返回的额外开销,所以相对慢一些 |
操 作 符 优 先 级 | 宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多些括 号。 | 函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。 |
带 有 副 作 用 的 参 数 | 参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果 | 函数参数只在传参的时候求值一次,结果更容易控制。 |
参 数 类 型 | 宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型 | 函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使他们执行的任务是 相同的。 |
调 试 | 宏是不方便调试的 | 函数是可以逐语句调试的 |
递 归 | 宏是不能递归的 | 函数是可以递归的 |
命名规定
宏名全部大写
函数名不要全部大写
3.3 条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令.
常见的条件编译指令:
1.
#if 常量表达式
//...
#endif
2.多个分支的条件编译
#if 常量表达式
//...
#elif 常量表达式
//...
#else 常量表达式
//...
#endif
3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif
3.4 文件包含
本地文件包含
#include "filename"
查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。
如果找不到就提示编译错误。
库文件包含
#include <filename.h>
查找头文件直接去标准路径下去查找,如果找不到就提示编译错误
#progma once可以避免头文件的重复引入