本篇博客接上篇《程序环境与预处理(1)》
目录
1.#define
1.1#define定义标识符
#define name stuff
举栗如下:
宏所表示的常量可以是数字、字符、字符串、表达式。其中最常用的是数字。
register int a=1; 明确声明必须要把变量存放在寄存器中,直到变量消失。 一般是默认register,大多数的情况下是不用写register。
注意:在定义标识符时不要在后面加 ;
如果加了就会导致如下结果,NUM替换的就是200;
1.2 #define定义宏
宏定义的声明方式
#define name(parament-list) stuff
注意 参数左括号必须与name紧邻 如果两者之间有空隙,参数列表就会被解释为stuff的一部分
下面也是一个容易出错的地方
由上面的问题可以看出,在使用宏定义的时候,不要吝啬括号
还有两个注意事项
1.宏,不能实现递归
2.当预处理搜索#define定义的符号时,字符常量内容并不被搜索
1.3 #和## (了解)
下面我们可以看看这段代码
可以发现字符串有自动连接的特点。
根据在个特点我们可以 使用#,把一个宏参数变成对应的字符串
#define PRINT(N, format) printf("the value of "#N" is "format"\n", N)
int main()
{
int a = 20;
double pai = 3.14;
PRINT(a, "%d");
PRINT(pai, "%lf");
return 0;
}
使用##,把位于它两边的符号合成一个符号
它允许宏定义从分离的文本片段创建标识符
#define CAT(name,num) name##num
int main()
{
int class105 = 200;
printf("%d\n", CAT(class, 105));
return 0;
}
1.4带副作用的宏参数
由此看出 当宏定义中 宏参数出现超过一次,如果参数带有副作用,你们使用该宏可能有危险,导致不可预测的结果,副作用就是表达式求值时出现的永久性效果。
1.5 宏和参数的对比
属性 | #define定义宏 | 函数 |
代码长度 | 在大多数时候使用程序都很长 | 函数代码只出现在一个地方,使用时调用那一个地方的代码 |
执行速度 | 更快 | 有函数调用和返回,相对慢一点 |
操作符优先级 | 需要多加些括号,预防出现不可预料的结果 | 函数参数只在调用时求值一次结果传给函数,表达式求值更容易预测 |
带有副作用的参数 | 参数可能被替换到宏的多个位置 | 函数参数只在求值时用一次,结果容易控制 |
参数类型 | 宏的参数和类型无关 | 参数和类型有关 |
调试 | 宏不方便调试 | 可以逐句调试 |
递归 | 不能递归 | 可以递归 |
宏通常被用于执行一些简单的运算。
1.6 命名约定
把宏名全部大写
函数名不要全部大写
2.#undef
#undef NAME
//如果现存的一个名字需要重新定义,需要先把旧名字移除
3. 命令行定义
许多C的编译器提供一种 允许在命令行中定义符号 的能力。用于启动编译的过程
4.条件编译
在编译一个程序的时候我们如果要将一条语句编译或放弃是很方便的,我们用条件编译指令就可以满足。
下面我们看看代码
#define MAX 0
int main()
{
#if defined (MAX)
printf("haha\n");
#endif
#if !defined(MAX)
printf("huhu\n");
#endif
#ifdef MAX
printf("huahua\n");
#endif
#ifndef MAX
printf("hlhl\n");
#endif
return 0;
}
常见的编译指令
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
#endif
5. 文件包含
5.1头文件被包含的方式
#include"filename"
先在源文件所在目录下找,如果未找到,编译器会在标准位置查找头文件(库函数头文件)
#include<filename>
直接去标准路径下查找
注意:对于库文件也可以用“”包含 只不过效率低 也不容易区分库文件还是本地文件
5.2 嵌套文件包含
这样的嵌套文件包含可以用条件编译来解决