自学内容网 自学内容网

#define定义

1.define定义常量

比如

#define max  100

我们就可以用max来代替100使用了。

#define saying "hello world"

我们就可以使用saying代替"hello,world"使用了

但是要注意的是,我们最好不要在最后面加上分号,有时候容易出错

比如

#include <stdio.h>
#define a 0;
int main(void)
{
int b;
if (0)
b = a;
else
b = 1;
return 0;
}

这个时候a替换过去就是

b=a;;

编译器会吧b=a;识别成一条语句

;识别出另一条语句(空语句)

这个时候if没加括号,就会导致if else使用出错。

2.define 定义宏

我们直接上例子理解

这个地方的宏类似于函数,但是又不同于函数(后面会解释) 

cal(a)就是 4*4=16没问题

但是为啥cal(b)就等于23呢?

#define无论是定义常量还是宏,都是预处理的时候直接替换的

也就是cal(b)=cal(3+5)=3+5*3+5=3+15+5=23;

因此我们在使用宏的时候,不要吝啬括号。

所以用于对数值表达式进行求值的宏定义都应用这种方式加上括号,避免在使用宏时由于参数中的操作符或临近操作符之间不可预料的相互作用,

 因此像这样改就可以了

但是要注意定义宏时要将宏的名字紧挨左括号,不然如果有空隙就成了定义常量了

注意:最好不要使用带有副作用的参数
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。

副作用就是表达式求值的时候出现的永久性效果。

#include <stdio.h>
#define cal(a,b) ((a++)>(b++)?(a++):(b++)) 
int main(void)
{
int a = 4;
int b = 3;
printf("%d\n", cal(a,b));
printf("%d\n", a);
return 0;
}

比如这串代码,虽然cal(a,b)的的值是5,但是此时再去打印a就是6了,如果我们不打印的话,就很容易将a的值当作5去使用,而且根据a和b的值大小不同,a++,b++的次数也不固定,很容易导致不可预测的后果,因此不要使用带有副作用的宏。

总结:

宏替换的规则
在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
1.在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。

如果是,它们首先被替换。
2.替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
3.最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上
述处理过程。
注意:
1.宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
2.当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被拽索

比如

#include <stdio.h>
#define cal(a,b) ((a++)>(b++)?(a++):(b++)) 
int main(void)
{
int a = 4;
int b = 3;
printf("cal(a,b)");
return 0;
}

这个时候打印出来的结果就是字符串cal(a,b)

3.宏和函数的优劣

接下来我们来看函数和宏的优缺点

和函数相比宏的劣势:
1.每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
2.宏是没法调试的。
3.宏由于类型无关,也就不够严谨。
4.宏可能会带来运算符优先级的问题,导致程容易出现错。

宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。

#include <stdio.h>
#include <stdlib.h>
#define MALLOC(n,type) (type*)malloc(n*sizeof(type)) 
int main(void)
{
int* a = MALLOC(20, int);
return 0;
}

宏这个地方就可以传类型了,这是函数做不到的

最后以一张表格来对比宏和函数优劣

3.#运算符和##运算符

(1)#运算符

、比如说我们希望做一个宏或者函数

假设int a=3  float b=4

比如输入 "%d",a

打印出来the value of a is 3

输入 "%f" ,b

打印出来是the value of b is 4

显然这用函数是无法实现的

因为the value of is 3

a

是不可变的,我再输入 "%f" ,b

打印出来的是the value of a is 4

那我们来一串宏试试可不可行

#include <stdio.h>
#include <stdlib.h>
#define WORK(format,n)  printf("the value of n is "format"\n",n)
int main(void)
{
int a = 4;
WORK("%d", a);
return 0;
}

 但是我们这个时候发现这个宏也无法解决这个问题,打印出来的是the value of n is 4

而不是the value of a is 4

 这个时候我们的#运算符就可以起到作用了,但是再此之前我们要回顾一点

就是两个连续的字符串会被自动连接一个字符串

因此我们就可以这样写代码

这样就可以达到我们的效果了

#运算符
运算符将宏的一个参数转换为字符串字面量。

它仅允许出现在带参数的宏的替换列表中。
#运算符所执行的操作可以理解为”字符串化“。

我们输入 "%d",a

会变成printf("the value of " "a" " is "format"\n",n);

(2)##运算符

## 运算符
## 可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符。#被称为记号粘合
这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。
比如说我们比较两个数的大小,int类型的值比较要写一个函数,浮点数类型的还要写一个函数,太麻烦了。

使用宏可以很好的解决问题

#include <stdio.h>
#include <stdlib.h>
#define GENERIC_MAX(type)\
type type##_max(type x,type y)\
{                             \
return (x > y ? x : y);       \
}
GENERIC_MAX(float)//替换到宏体后float##_max生成新的符号 int_max做函数名
GENERIC_MAX(int)//替换到宏体后int##_max生成新的符号 float_max做函数名
int main(void)
{
//调用函数
int m = int_max(2, 3);
printf("%d\n", m);
float n = float_max(2.5f, 3.5f);
printf("%f\n", n);
return 0;
}

一个宏声明未结束,句尾要加上\符

(3) #undef
这条指令用于移除一个宏定义。

(4)命名约定
-般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。
那我们平时的一个习惯是:
把宏名全部大写
函数名不要全部大写


原文地址:https://blog.csdn.net/Starry_tsx/article/details/142436804

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!