预处理
一,预定义符号
C语言中有一些预定义符号,这些符号是语言内置的,可以直接使用
例如:__FILE__ __LINE__ __TIME__ __DATE__ __STDC__(如果编译器支持ANSI C就返回1
,否则未定义)
int main(){printf("%s\n", __FILE__);printf("%d\n", __LINE__);printf("%s\n", __TIME__);printf("%s\n", __DATE__);//printf("%d\n", __STDC__);return0;}
可以看到我的这个编译器是不支持ANSI C的
二,#define
1,#define定义标识符
语法:#define name stuff
#define定义的标识符,可以代表很多东西
例如:
#define M10
#define Q"hello world"
#define PRINTprintf("hello world")
int main(){printf("%d\n",M);printf("%s\n",Q);PRINT;return0;}
上面那些都是正常的使用情况:
下面介绍两种特殊的情况:
1,定义一个死循环
2,如果定义的stuff过长,可以分几行写,除最后一行外,每行最后加上\作为分行符
#define DO_FOEVERfor(;;)
#define DEBUG_PRINTprintf("file:%s\tline:%d\t \
date:%s\ttime:%s\n",\
__ FILE__,__LINE__ , \
__DATE__,__TIME__ )
2,#define定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,
这种实现通常称为宏(macro)或定义 宏(define macro)
语法:#define M(参数) stuff
无论在定义宏还是普通标识符的时候,最终都会在编译预处理阶段完成替换
并且在定义宏的时候,应注意带括号,防止运算符的优先级问题
下面举几个例子,自己体会一下:
#define DOUBLE(x) x*x
int main(){printf("%d\n",DOUBLE(6));printf("%d\n",DOUBLE(6+1));printf("%d\n",2*DOUBLE(6));return0;}
看到这个结果明显不对劲,这就是上述所说的应该给宏定义的文本中的各项加上括号,避免出错
下面看下修改过后:
#define DOUBLE(x)((x)*(x))
注意:无论在定义标识符还是在定义宏的时候,最好不要在结尾加上分号,
这样很容易在使用上出错,例如:
#define DOUBLE(x)((x)*(x));if(1)printf("%d\n",DOUBLE(6));else;
实质上,编译预处理完后,DOUBLE(6)被替换成下面的样子
if(1)printf("%d\n",((6)*(6)));;else;
上面这种情况是会报错的,一条if语句跟了两条语句(没有大括号前提下)。
所以,我们在定义标识符,或者宏的时候 末尾不要加分号,避免使用时的错误。
3,#
讲述这个之前,首先介绍一下C语言的一种机制:
字符串相邻在一起时,会合并成一个字符串
例如:
int main(){printf("%s\n","hello ""world");return0;}
而,#的作用是,当宏参数前面加上#的时候,会将这个参数转化为字符串。
例如:#define(x) printf("..."#x"...");
最终会被替换成:printf("...""x""...");
下面举一个业务场景做例子:
int main(){
int a =10;printf("the value of a is %d\n", a);
int b =20;printf("the value of b is %d\n", b);
float c =30.0f;printf("the value of c is %f\n", c);return0;}
每一个数据后面都跟着一条输出语句,而且每条输出语句的内容极为相似,
我们能不能将它封装为一个函数呢?其实是不可以的,因为我们想让它以何种方式打印,
需要传给函数"%d" "%f"这样的参数,但是函数会误以为这些是字符串。
所以要借助宏来实现
#define PRINT(x,format)printf("the value of "#x" is "format"\n",x)
int main(){
int a =10;PRINT(a,"%d");
int b =20;PRINT(b,"%d");
float c =30.0f;PRINT(c,"%f");return0;}
4,##
##的作用非常奇怪,是将##两边的字符合成一个字符
例如:
#define A(x,y)printf("%d\n",x##y)
int main(){
int ab =10;A(a, b);return0;}
5,带副作用的宏参数
当宏的参数在宏定义的文本中出现了不止一次的时候,就可能会出现副作用。
例如:我们定义一个宏来输出两个数中的较大值
#define MAX(x,y)((x)>(y)?(x):(y))
int main(){
int a =2;
int b =3;//想输出++a 与++b中的较大值printf("%d\n",MAX(++a,++b));return0;}
我们本想输出的是4,但是你看结果,这带副作用的参数
6,只能靠宏实现而函数实现不了的功能
(1)对malloc进行一次封装
比如,我们正常用malloc来开辟内存空间
int* a=(int*) malloc(sizeof(int)*10);
写起来较为麻烦,可以用宏来封装一层
#define MALLOC(type,num)(type*)malloc(sizeof(type)*num)
这样开辟内存空间就可以这样写了,
int*a=MALLOC(int,10);
比上面的代码就简洁了许多
(2)offsetof的实现
我们在结构体那片博客中介绍过offsetof()的功能:是结构体变量的成员地址
相对于结构体变量的地址的偏移量
今天,我们就来模拟实现一下offsetof()
struct stu
{
int a;
char c;
int b;};
#define OFFSETOF(type,x)(int)&(((type*)0)->x)
int main(){printf("%d\n",OFFSETOF(struct stu, a));printf("%d\n",OFFSETOF(struct stu, c));printf("%d\n",OFFSETOF(struct stu, b));return0;}
所以,可以看到函数与宏各有千秋,函数不能实现的宏可以实现,宏不能实现的但是函数可以实现,
所以下面我们来总结一下,函数与宏的区别
(3)特殊场景
#define PRINT(x,format)printf("the value of "#x" is "format"\n",x)
int main(){
int a =10;PRINT(a,"%d");
int b =20;PRINT(b,"%d");
float c =30.0f;PRINT(c,"%f");return0;}
三,函数与宏的比较
1,代码长度
宏:每次使用时,在编译预处理阶段定义的宏的代码就会插入到程序中,如果定义的宏代码量很大
,这样的话就大大增加了代码量。
函数:函数的代码只出现在一个地方,每次调用函数的时候都只会调用这一份代码
2,执行速度
宏:当实现的功能比较简单是,宏的速度是很快的
函数:当实现的功能比较简单时,调用函数和释放函数栈帧的时间,比真正实现函数功能还长
举个简单例子:输出较大值
int main(){
int a =2;
int b =3;max(a, b);MAX(a, b);return0;}
可以看到,如果实现简单的功能,宏要比函数运行速度快不少
3,操作符的优先级
宏:如果在定义宏的时候没有适当加上括号,可能会在周围环境的表达式中,与临近运算符出现
未曾预料的结果
函数:函数参数只在函数调用的时候计算一次,它的运算结果传递给函数。表达式的结果更容易预测。
例如:
#define DOUBLE(x) x+x
int main(){
int a =3;printf("%d\n",2*DOUBLE(3));}
我们预料的结果应该是12,但结果:
4,带有副作用的参数
宏:宏参数可能被替换到宏体中的多个位置,所以带有副作用的参数会造成不可预料的结果。
函数:函数只在传参的时候求值一次,结果更容易预料。
5,参数类型
宏:参数与类型无关,只要对参数的操作是合法的,他就可以运用到各种类型。
函数:函数参数是与类型有关的,参数的类型不同那么就需要不同的函数。
例如:比较大小用宏来实现,就可以比较多种数据类型的大小
#define MAX(x,y)((x)>(y)?(x):(y))
6,调试。
宏:由于宏的实现是替换,所以宏不能够调试。
函数:函数是可以调试的。我们在调试的时候按F11进入到函数内部。
voidprint(int* arr, int len){
int i =0;for(i =0; i < len; i++){printf("%d ", arr[i]);}}
int main(){
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int sz =sizeof(arr)/sizeof(arr[0]);print(arr, sz);return0;}
7,递归
宏:宏是不能够递归的。
函数:函数可以实现递归。
例如我们写一个递归函数实现求前N项和
int sumN(int n){if(n ==1){return1;}else{return n +sumN(n -1);}}
int main(){
int n =10;printf("%d\n",sumN(n));return0;}
综上,介绍了七点函数与宏的区别。
本文转载自: https://blog.csdn.net/Djsnxbjans/article/details/127345463
版权归原作者 大理寺j 所有, 如有侵权,请联系我们删除。
版权归原作者 大理寺j 所有, 如有侵权,请联系我们删除。