目录
12.2.1 宏定义
编译与预处理指令
- #开头的都属于编译预处理指令(它们不是C语言的成分,但C语言离不开它们)
- #define用来定义一个宏
- 编译预处理的流程:.c->.i(编译预处理完成)->.s(编译器编译,汇编代码文件)->.o(目标代码文件)->a.out(完成链接,生成可执行文件)
#include<stdio.h>
//当你需要定义一个常量...
const double PI = 3.14159;//C99
#define PI 3.14159//老版本C,
//定义一个符号————宏,名为PI,值为3.14159
编译预处理阶段会将所有PI替换为3.14159,简单的文本替换
int main()
{
printf("%f\n",2*PI*3.0);
return 0;
}
#define
- #define <名字> <值> (不需要等号。也没有分号,因为不属于C语句)
- 名字必须是一个单词,值可以是任意的类型
- 一个编译预处理指令,在编译之前完成文本替换
宏
- 如果一个宏里有别的宏名字,会被替换
- 如果一个宏的值超过一行,最后一行之前的行末要加反斜杠
- 宏的值后面出现的注释不会被当作宏的值
#include<stdio.h>
#define PI 3.14159
#define PI2 2*PI //就是Π乘以2
#define PRINT printf("%f\n",2*PI*3.0);\//此时就是分行的写法,第一句得写分号
printf("%f\n",2*PI*3.0)
int main()
{
printf("%f\n",2*PI*3.0);
printf("%f\n",PI2*3.0);
PRINT;
return 0;
}
三次结果完全相同。
没有值的宏
- #define _DEBUG,告诉编译器有这个宏,但不需要有值
- 这类宏是用于条件编译的,后面有其他的编译预处理指令来检查这个宏是否被定义过
一些预定义的宏
- _LINE_ //用来获取行号
- _FILE_ //用来获取文件名
- _DATE_ //用来获取编译时的日期
- _TIME_ //用来获取编译时的时间
- _STDC_
试试这个程序吧!
#include<stdio.h>
int main()
{
printf("%s:%d\n",_FILE_,_LINE_);
printf("%s:%d\n",_DATE_,_TIME_);
return 0;
}
12.2.2 带参数的宏
除了简单的替换文本的宏,还可以定义一些带参数的
像函数的宏
#include<stdio.h>
#define cube(x) ((x)*(x)*(x))//名字和值都可以带参数,原理一样是替换
int main()
{
printf("%d\n",cube(5));//是不是很像调用函数?
return 0;
}
另一个程序,同样的预编译语句
int main()
{
int i;
scanf("%d",&i);
printf("%d\n",cube(i+2));//此处会被替换为
//((i+2)*(i+2)*(i+2))
return 0;
}
看起来相当完美,你甚至不需要写函数了!然而新手是会犯错的
错误定义的宏
#define RadToDeg1(x) (x*57.29578)
#define RadToDeg2(x) (x)*57.29578
int main()
{
printf("%f\n",RadToDeg1(5+2));//x=7 大概400多
printf("%f\n",180/RadToDeg2(1));//180除以57,等于3左右
return 0;
}
怎么会相差如此之多?我们需要看看它被替换成了什么
我们的本意是让180先除以57.多,然后再乘1,但是运算反了,这是为什么?
带参数的宏的原则
#define RadToDeg1(x) (x*57.29578)
#define RadToDeg2(x) (x)*57.29578
仔细观察下,第二句之所以出错,是因为57点多暴露在外面,因此原则上,带参数的宏必须套上括号
- 一切都要有括号(整个宏需要有括号,每个参数需要有括号)
- 所以上面的两个宏都不规范,应该写为:
#define RadToDeg1(x) ((x)*57.29578)
带多个参数的宏
- 一个宏可以带多个参数,也可以组合(嵌套)使用其他宏
#define MIN(a,b) ((a)>(b)?(b):(a))
分号?
图中的三个分号,写上之后各个语句都被分开,是不能使用的
带参数宏小知识
- 在大型程序中非常常见,可以非常复杂(代替函数,牺牲空间换取效率,有#和##的帮助)
- 中西程序员对其应用程度不同,属于小的文化差异。西方人会将其做的相当复杂
- 传递参数时没有类型检查,inline函数机制可以取代宏定义方式,可以帮忙解决这种问题