【C语言】#define的作用


1.#define定义标识符

  1. 语法
#define name stuff
  1. 例子
#define M 1000
int main()
{
	int m = M;
	printf("%d",m);
	return 0;
}

除此之外,#define还可以定义关键字、数据类型以及语句。

struct Stu
{
	char name[20];
	int age;
};
#define STU struct Stu//创建一个更简短的名字,类似于typedef
int main()
{
	STU s = { "zhangsan",18 };
	return 0;
}


#define do_forever for(;;)
int main()
{
	do_forever;//死循环
}


#define CASE break;case//防止有人忘记写break
switch(num)
{
	case 1:
	CASE 2;
	CASE 3;
}
  1. 疑惑

#define后面为什么不加;?
(1)可以加上分号,但不建议加。#define后加上分号后,在使用它定义的符号的时候就别加上分号。

#define M 1000;
int main()
{
	int m = M
	return 0;
}

(2)或者继续加上分号,我们知道在预处理阶段#define定义的符号会被替换,替换后变成int m = 1000;;,后面的分号相当于空语句,这样也不会有错。但是存在隐患。

#define M 1000;
int main()
{
	int m = M;
	return 0;
}
//隐患
if(condition)
max = M;//在没有跟大括号的情况下,if后只能跟一条语句,加上空语句else就不知道与谁匹配,容易出错
else
max = 0;

总结
#define后不建议加分号。


2.#define定义宏

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。

  1. 语法
#define name( parament-list ) stuff
//parament-list是参数列表,可能出现在stuff
//注意:左边的括号要紧邻name,否则会被当作stuff的一部分
  1. 例子1
#define SQUARE(X) X*X
int main()
{
	int a = SQUARE(3);
	printf("%d", a);
	return 0;
}

结果
9

宏定义在预处理阶段会被替换,变成int a = 3*3。

  1. 例子2
#define SQUARE(X) X*X
int main()
{
	int a = SQUARE(3+1);
	printf("%d", a);
	return 0;
}

结果
7

为什么?难道不是4 * 4=16吗?我们要了解宏定义的参数是先传过去,再计算的。3+1传给X,变成int a = X * X = 3+1 * 3+1。所以我们在定义的时候记得给参数加上括号,就像这样,#define SQUARE(X) (X) * (X)。

  1. 例子3
#define DOUBLE(X) (X)*(X)
int main()
{
	printf("%d", 10 * DOUBLE(4));
	return 0;
}

结果
44

在预处理阶段,宏被替换,变成10*(4)+(4),所以才有44.。所以我们在定义的时候记得用括号把整个文本括起来,就像这样,#define DOUBLE(X) ((X)+(X))

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


3.undef

  1. 语法
undef NAME//作用是取消定义
  1. 例子
#define M 100
int main()
{
	int a = M;
#undef M
	pprintf("%d", M);
	return 0;
}

结果
在这里插入图片描述


4.#和##

4.1#的作用

  1. 先在这里拓展一点小知识。
int main()
{
	char* p = "hello ""world";
	printf("%s\n", p);
	printf("hello"" world");
	return 0;
}

结果
hello world
hello world

我们发现字符串是可以自动连接的。

  1. 我们现在来写一个宏,它的作用是打印出这样一句话:the value of 变量名 is 该变量的值。通过这个宏我们就可以了解#的作用了。
#define PRINT(X) printf("the value "#X" is %d\n",X)
int main()
{
	int a = 10;
	PRINT(a);
	int b = 20;
	PRINT(b);
	int c = 30;
	PRINT(c);
	return 0;
}

结果
在这里插入图片描述

现在,通过前面小知识的铺垫,我们不难想到#的作用:把一个宏参数变成对应的字符串。原理就是"the value of “#a” is %d"=="the value of "“a”“is %d”。

4.2##的作用

  1. ##的作用也很奇葩:##可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。
  2. 例子
#define CAT(X,Y) X##Y
int main()
{
	int ZhangsanHeight = 180;
	int ret = CAT(Zhangsan, Height);//##把这两个参数合为一体
	printf("%d", ret);
	return 0;
}

结果
180

注意
这样的宏创建的标识符必须是合法的(有定义的)。否则会报错。
在这里插入图片描述


5.带副作用的宏参数

我们都知道++和–这些操作符带有副作用,因为其会永久地改变操作数。现在我们来看看这些操作符作用于宏参数会发生什么。

#define MAX(X,Y) ((X)>(Y)?(X):(Y))
int main()
{
	int a = 5;
	int b = 8;
	int m = MAX(a++, b++);
	printf("a = %d,b = %d,m = %d\n", a, b, m);
}

结果
在这里插入图片描述

分析
前面我们就说过这些参数是先传过去再计算的,在预处理阶段,变成int m = ((a++)>(b++)?(a++):(b++))。先判断(a = 5)>(b = 8)为假,再进行++,此时a = 6,b = 9,因为为假所以执行b++,先把b赋值给m,此时m = 9,再进行++,此时b = 10。
总结
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。


6.宏与函数的对比

  1. 在上面的例子中,我们为什么不使用函数来比较两个参数的大小?换种说法宏相较于函数有何优点?
    (1)函数在调用过程中需要进行大量准备,需要传参压栈,需要返回,比起宏更加复杂。宏比函数在程序的规模和速度方面更胜一筹。
    (2)函数参数必须声明类型,像这个比较大小的例子,参数都是整形,只能比较整形的大小,而宏可以用来比较浮点型等可以用>来比较的类型。宏是类型无关的。
  2. 那么宏又有那些缺点(与函数相比)呢?
    (1)宏在预处理阶段会被替换,每次调用宏都会被替换,如果替换文本非常长,程序的长度会大幅增长。
    (2)宏是没法调试的。宏早早地就消失在预处理阶段,等到调试阶段怎么可能见到它。
    (3)宏由于类型无关,也就不够严谨。双刃剑呐。
    (4) 宏可能会带来运算符优先级的问题,导致程容易出现错。前面定义宏的三道例题无疑验证这点。

总结
如果运算逻辑很简单可以用宏,如果运算逻辑复杂,可以用函数。


6.宏的命名

看到这里你们肯定发现宏的命名都是大写的,而函数的命名不全是大写的。


7.其他预处理指令

这里就简单讲下常见的条件编译指令。

  1. 什么是条件编译?
    简单讲就是满足条件就编译,不满足就不编译。
  2. 常见的条件编译指令

(1)
语法
#if 常量表达式
……
#endif
例子

int main()
{
#if 1       //常量表达式为真下面的代码就参与编译,为假就不参与编译
	printf("hello\n");
#endif      //相当于结束标志
	return 0;
}

结果
hello

(2)
语法
#if 常量表达式
……
#elif 常量表达式
……
#else
……
#endif
例子

int main()
{
#if 1+1==0                   //注意越往上的常量表达式如果为真,
	printf("haha\n");        //只执行紧跟的代码,后面的代码不用计算。
#elif 1+1==1                 //像本题的第一个和第二个常量表达式都为真,
	printf("hehe\n");        //但只执行第一个常量表达式后面的代码。
#else 
	printf("liu\n");
#endif
	return 0;
}

结果
haha

(3)
语法
#ifdef 宏
……
#endif
例子

#define TEST 6
int main()
{
#ifdef TEST      //如果有定义这个宏,下面的代码就参与编译。
	printf("test");
#endif
	return 0;
}
//也可以写成
#if define(TEST)
	printf("test");
#endif

结果
test

(4)
语法

#ifndef 宏
……
#endif
例子

int main()
{
#ifndef TEST        //如果没有定义这个宏,下面的代码就参与编译
	printf("test");
#endif
	return 0;
}
//同样可以写成
#if !define(TEST)
		printf("test");
#endif
  1. 还有其他预处理指令这里就不详讲了,在这里推荐一本书《C语言深度解剖》,里面有讲到一些我们在课堂上学不到的。

猜你喜欢

转载自blog.csdn.net/Zhuang_N/article/details/128904778