个人主页:strive-debug
宏的补充
https://blog.youkuaiyun.com/2401_85286030/article/details/146239880?spm=1001.2014.3001.5501
宏定义注意事项
- 括号的重要性:在定义宏时,务必加上括号。这是因为在使用宏进行运算时,会涉及到运算符的优先级问题。如果不加括号,可能会得到意想不到的结果。例如:
#define DEFULL(n) n*n//(n)*(n)加上括号就可以了,不会受到运算符优先级的影响了
int main()
{
int n = 0;
scanf("%d", &n);
int ret = DEFULL(n);
printf("%d", ret);//如果你输入的是3+1的话传过去之后你得到的值不是你想得到的16,而是7
return 0;
}
//还有一种情况就是((n)*(n))的情况
//如果你在搞个int ret=10*DEFULL(n)的话
//宏定义的是(n)+(n)的时候,运算符优先级会先计算*号的
//这里就有涉及到计算机的预处理了,计算机不是把数传给宏,然后让宏计算完再传回来
//而是直接在 int ret=10*DEFULL处把DEFULL替换掉变成int ret=10*n+n
//所以括号的重要性就体现出来了((n)+(n))
- 避免使用分号:在宏定义中,最好不要加分号。因为宏只是简单的文本替换,如果加了分号,可能会导致语法错误或逻辑错误。
宏的副作用
- 参数自增问题:在日常使用中,`a+1` 和 `a++` 都是让 `a` 增加 1,但在宏的使用中,`a++` 形式会导致副作用。因为宏有它自己的参数,它会保存 `a` 的值。如果多次使用这个宏,`a` 会不断增加,从而得不到预期的结果。
宏不能出现递归
- 递归问题:宏不能直接实现递归,因为宏展开是静态的,不支持动态的条件判断和递归调用。例如:
#define MAX(x,y) ((x)>(y)?(x):(y))
//int main()
//{
// int a = 10;
// int b = 20;
// int ret = MAX(a++, b++);
// printf("a=%d b=%d", a, b);
// return 0;
//}
上述代码中,宏 `MAX` 无法正确处理 `a++` 和 `b++` 的递归递增。
宏和函数的区别
- 优势:
1. 性能:宏在一些简单的运算上比函数更占优势,因为它省去了函数调用和返回值的开销。
2. 类型无关:宏的参数与类型无关,不需要显式声明类型,增加了灵活性。
#include<stdio.h>
#include<stdlib.h>
#define MALLOC(n,type) (type*)malloc(n*sizeof(type))
int main()
{
//int*p=(int*)malloc(10*sizeof(int));
int*p=MALLOC(10,int);
return 0;
}//
- 劣势:
1. 代码膨胀:每次使用宏都会将宏的代码替换进程序里,如果代码较长,会增加程序的长度。而函数的调用不会这样。
2. 调试困难:宏不能被调试,因为它们在预处理阶段就被替换了。
3. 类型不安全:由于与类型无关,宏不够严谨,可能导致类型相关的问题。
4. 运算符优先级问题:宏可能会带来运算符优先级的问题,导致程序出错。
宏和函数的使用建议
- 简单逻辑使用宏:当实现的逻辑比较简单时,可以使用宏来提高效率。
- 复杂逻辑使用函数:如果函数和宏的时间相差不大,还是建议使用函数,因为函数的优势和好处更多。
通过合理使用宏和函数,可以提高代码的效率和可维护性。
#和##运算符
#运算符
#define PRINT(format,n) printf("the value of " #n " is " format"\n",n)
//这里就利用了宏无类型
int main()
{
int a = 10;
PRINT("%d", a);
int b = 9;
PRINT("%d", b);
float f = 9.9f;
PRINT("%f", f);
return 0;
}
## 运算符
//创建一个生成函数的模版
#define GENERIC_MAX(type)\
type type##_max(type x, type y)\
{ \
return x>y?x:y;\
}
//使用上面的模版
//生成为int类型的函数
GENERIC_MAX(int)
//生成为float类型的函数
GENERIC_MAX(float)
int main()
{
printf("%d\n", int_max(3, 5));
printf("%f\n", float_max(3.5, 5, 0));
}
这是有关#\##运算符的相关用法,日常不一定要使用但是大家要知道。
命名的区分
如果全是大写的那就是宏的名称。
但也是有部分特例offsetof
如果不全是大写的那就是函数的名称。
注:这是约定俗成的东西,如果你不按照这个规则写,也没事,但是会大大减少可读性。
#undef
#define M 10
int main()
{
printf("%d", M);
#undef M
//printf("%d", M);//会报错
}