【 C 】宏 简记

举报
李锐博恩 发表于 2021/07/15 05:49:16 2021/07/15
【摘要】 目录   #define指令 宏 #define 替换 宏与函数 带副作用的宏参数 命名约定 #undef #define指令 #define name stuff 有了这条指令以后,每当有符号 name 出现在这条指令后面,预处理器就会把它替换为几个 stuff。 最简单的例子: 为数值命名一个符号: #define M 6 当然...

目录

 

#define指令

#define 替换

宏与函数

带副作用的宏参数

命名约定

#undef


#define指令

#define name stuff

 

有了这条指令以后,每当有符号 name 出现在这条指令后面,预处理器就会把它替换为几个 stuff。

最简单的例子:

为数值命名一个符号:

#define M 6
 

当然作用不限于此:使用#define指令,你可以把任何文本替换到程序中。

例子:

#define CASE break;case
 

它自动把break放在每个 case 之前,这样就使得switch语句看上去更像其他语言中的case语句。


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

下面是宏的声明方式:

#define name( parameter_list) stuff
 

其中, parameter_list 是一个由逗号分隔的符号列表,它们可能出现在 stuff 中。参数列表的左括号必须与 name 紧邻。如果两者之间没有任何空白存在,参数列表就会被解释为 stuff 的一部分。

当宏被调用时,名字后面是一个由逗号分隔的值的列表,每个参数都与宏定义中的一个参数相对应,整个列表用一对括号包围。当参数出现在程序中时,与每个参数对应的实际值都将被替换到 stuff 中。

例如:

#define SQUARE(x) x * x

 

如果在上述声明之后,你把 SQUARE(5)置于程序中,预处理器就会用下面的表达式替换上面的表达式: 5 * 5;

警告几个陷阱:

上面的这个宏,有时候的替换操作也许和你想象的不一样:


      a = 5;
      printf( "%d\n", SQUARE( a + 1 ) );
  
 

你也许会这么运算,(a + 1)* (a + 1)= 36,但实际上的运算是 a + 1*a + 1 = 11。

很简单,如果你想做(a + 1)* (a + 1)= 36 这种运算,你的宏可以这样表达:

#define SQUARE(x) (x) * (x)

 

再举一个例子:

这里有一个宏定义:

#define DOUBLE(x) (x) + (x)
 

再看看上述定义后面的这段代码打印什么?


      a = 5;
      printf( "%d\n", 10*DOUBLE(a) );
  
 

你也许会认为这么运算: 10 * ((a) + (a))= 100,可这只是你的想象罢了,你要按实际情况替换:

10 * (a) + (a)=55;

如果你想实现:10 * ((a) + (a))这种运算,你必须这样定义宏:

 

#define DOUBLE(x) ( (x) + (x) )
 

括号一个也不能少!

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


#define 替换

在程序中扩展 #define 定义符号和宏时,需要设计几个步骤:

1. 在调用宏时,首先对参数进行检查,看看是否包含了由 #define 定义的符号。如果是,它们首先被替换。

2. 替换文本随后被插入到程序中原来文本的的位置。对于宏,参数名被它们的值所代替。

3. 最后,再次对结果文本进行扫描,看看它是否包含了任何由 #define 定义的符号。 如果是,重复上述过程。

这样,宏参数和#define定义可以包含其他#define定义的符号,但是,宏不可以出现递归!

进一步内容,见《c与指针》相关描述!


宏与函数

宏非常频繁地用于执行简单的运算,比如在一个表达式中寻找其中较大的一个:

#define MAX( a, b ) ( (a) > (b) ? (a) : (b) )
 

为什么不用函数来做?

最重要的是,函数参数必须声明为特定的类型,所以它只能在类型合适的表达式上用。反之上面的这个宏可以用于各种类型。换句话说,宏是与类型无关的。

和使用函数相比,宏的不利之处在于每次使用宏时,一份宏定义代码的拷贝都插入到程序中。除非宏非常短,否则使用宏可能会大幅度增加程序的长度。

还有一些任务根本无法用函数实现,如下:


      #define MALLOC( n, type ) \
     
      ( (type *)malloc( (n) * sizeof( type ) ) )
  
 

这个宏的第2个参数是一种类型,它无法作为参数进行传递。


带副作用的宏参数

当宏参数在宏定义中出现的次数超过一次时,如果这个参数具有副作用,那么当你使用这个宏时,就有可能出现危险,导致不可预料的结果。

副作用就是在表达式求值时,出现的永久性结果。这句话不好理解,看下面的例子:

这个表达式:x + 1 不具有副作用,因为这个表达式无论重复执行多少次,它每次获得的结果都是一样的,也就是这个x不会变。

而下面这个表达式:x++ 具有副作用,因为它增加x的值。当这个表达式下一次执行时,它将产生一个不同的结果。

看下面的一个例子:

MAX宏可以证明具有副作用的参数所引起的问题:


      #define MAX( a, b ) ( (a) > (b) ? (a) : (b) )
      ...
      x = 5;
      y = 8;
      z = MAX( x++, y++ );
      printf( "x = %d, y = %d, z = %d\n", x, y, z );
  
 

这是一个陷阱,你可能就认为 x = 6, y = 9, z = 9了,实际上呢?并非如此,x = 6, y = 10, z = 9;

是不是大吃一惊了,这究竟是为什么呢?

我们不妨把那个宏定义的地方展开:

z = ( ( x++ ) > ( y++ ) ? ( x++ ) : ( y++) );
 

如果你对后缀++了解的话,那么结果就一目了然了。

具体于这个例子的数值时,也就是x = 5, y = 8,x在这个表达式执行了一次,而y执行了两次,而最后一次是y的一份拷贝赋值给z后,再自增的。


命名约定

使用宏的语法和使用函数的语法完全一样,如何区分两者呢?

我们约定宏名字全部大写,如下:

value = max( a, b ); 是函数的调用。

value = MAX( a, b ); 是宏调用。


#undef

这条预处理指令用于移除一条宏定义。

#undef name

如果一个现存的名字需要重新定义,那么它的旧定义首先必须用#undef移除。

 

 

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/82713541

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。