C语言中:使用宏包括单双井(#&&##)号可变参数

本文详细介绍了C语言中宏的高级应用,包括警告输出、连接符的使用、以及变参宏的实现技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Warning" "divider == 0" "/n");

  } while(0);

  这样每次divider(除数)为0的时候便会在标准错误流上输出一个提示信息。

  而##被称为连接符(concatenator),用来将两个Token连接为一个Token。注意这里连接的对象是Token就行,而不一定是宏的变量。比如你要做一个菜单项命令名和函数指针组成的结构体的数组,并且希望在函数名和菜单项命令名之间有直观的、名字上的关系。那么下面的代码就非常实用:

  struct command

  {

  char * name;

  void (*function) (void);

  };

  #define COMMAND(NAME) { NAME, NAME ## _command }

  //然后你就用一些预先定义好的命令来方便的初始化一个command结构的数组了:

  struct command commands[] = {

  COMMAND(quit),

  COMMAND(help),

  ...

  }

  COMMAND宏在这里充当一个代码生成器的作用,这样可以在一定程度上减少代码密度,间接地也可以减少不留心所造成的错误。我们还可以n个##符号连接 n+1个Token,这个特性也是#符号所不具备的。比如:

  #define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d

  typedef struct _record_type LINK_MULTIPLE(name,company,position,salary);

  //这里这个语句将展开为:

  // typedef struct _record_type name_company_position_salary;

  关于...的使用

  ...在C宏中称为Variadic Macro,也就是变参宏。比如:

  #define myprintf(templt,...) fprintf(stderr,templt,__VA_ARGS__)

  //或者

  #define myprintf(templt,args...) fprintf(stderr,templt,args)

  第一个宏中由于没有对变参起名,我们用默认的宏__VA_ARGS__来替代它。第二个宏中,我们显式地命名变参为args,那么我们在宏定义中就可以用 args来代指变参了。同C语言的stdcall一样,变参必须作为参数表的最有一项出现。当上面的宏中我们只能提供第一个参数templt时,C标准要求我们必须写成:

  myprintf(templt,);

  的形式。这时的替换过程为:

  myprintf("Error!/n",);

  替换为:

  fprintf(stderr,"Error!/n",);

  这是一个语法错误,不能正常编译。这个问题一般有两个解决方法。首先,GNU CPP提供的解决方法允许上面的宏调用写成:

  myprintf(templt);

  而它将会被通过替换变成:

  fprintf(stderr,"Error!/n",);

  很明显,这里仍然会产生编译错误(非本例的某些情况下不会产生编译错误)。除了这种方式外,c99和GNU CPP都支持下面的宏定义方式:

  #define myprintf(templt, ...) fprintf(stderr,templt, ##__VAR_ARGS__)

  这时,##这个连接符号充当的作用就是当__VAR_ARGS__为空的时候,消除前面的那个逗号。那么此时的翻译过程如下:

  myprintf(templt);

  被转化为:

  fprintf(stderr,templt);

  这样如果templt合法,将不会产生编译错误。 这里列出了一些宏使用中容易出错的地方,以及合适的使用方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值