`
javatoyou
  • 浏览: 1083520 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

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合法,将不会产生编译错误。 这里列出了一些宏使用中容易出错的地方,以及合适的使用方式。

分享到:
评论

相关推荐

    C语言宏定义##连接符和#符的使用

    - **操作符优先级**:在宏定义中使用某些操作符时需要注意优先级问题,以免造成误解或编译错误。 - **副作用的重复**:如果宏定义中的表达式有副作用,则多次展开可能会导致这些副作用被重复执行,从而产生意料之外...

    C语言中宏定义"#"和"##"符号的用法.doc

    总结来说,C语言中的`#`和`##`符号是宏定义中的重要工具,它们分别用于字符串化和拼接操作,使得宏定义能处理更复杂的文本替换,包括生成唯一的变量名和结构体填充等高级应用场景。理解和熟练运用这两个符号,能够...

    C语言中宏定义"#"和"##"符号的用法

    * 如果宏定义中使用了"#"或"##"符号,那么宏参数将不会再展开。 * 如果宏参数是一个宏,那么需要添加一个中间转换宏,以确保宏参数的正确展开。 五、应用实例 以下是一些使用"#"和"##"符号的应用实例: * 合并...

    C语言宏定义中#与##运算符

    需要注意的是,如果在宏定义中使用了 `##` 运算符,那么参数在替换过程中不会被扩展。这意味着嵌套调用可能会导致意外的结果。例如: ```c #define CONCAT(x, y) x##y ``` 对于 `CONCAT(a, CONCAT(b, c))` 的调用...

    c语言中“#” “##”的使用方法与区别

    在C语言中,“#”和“##”是预处理器的一部分,它们分别具有将宏参数转换为字符串以及连接宏参数的功能。正确理解并掌握这两种符号的使用方法对于深入理解和编写复杂的C语言程序至关重要。 #### 二、“#”的使用...

    C语言中可变参数的用法

    这些宏定义在`stdarg.h`头文件中,因此编写可变参数函数时需要包含此头文件。 #### 宏定义解析 1. **`va_start`**: 初始化可变参数列表的迭代器。 - `void va_start(va_list arg_ptr, prev_param);` - 参数: -...

    C语言可变参数

    在C语言中,可变参数是一种非常有用的特性,它允许函数接受不同数量的参数。这种特性使得编写如`printf`函数那样灵活的函数成为可能,这些函数可以根据传入的参数动态地处理数据。本文将深入探讨C语言中的可变参数...

    C语言宏:定义、使用及其最佳实践

    例如,在宏表达式中使用副作用强烈的操作(如自增或自减)可能导致问题。下面的示例演示了这种情况: ```c #define ADD(x, y) (x + y) int main() { int a = 5, b = 3; printf("Result: %d\n", ADD(a++, b++)); ...

    C语言可变参数的使用

    在C语言中,可变参数是一种非常有用的特性,它允许函数接受不同数量的参数。这种功能在编写一些需要灵活处理参数的函数时非常方便,比如常见的`printf`函数。本篇将详细介绍C语言中可变参数的使用及其背后的机制。 ...

    C语言中“#”和“##”的用法.pdf

    `GET_FILE_NAME`宏用于将`__FILE__`的值展开,以便能够在代码中使用当前文件名。 ### 总结 C语言中的“#”和“##”预处理器指令提供了强大的功能,用于宏定义和宏展开。它们能够帮助开发者编写更为灵活和模块化的...

    c语言带参数的宏定义

    ### C语言带参数的宏定义详解 #### 一、引言 C语言作为一种广泛使用的编程语言,提供了多种机制来提高代码的复用性和可维护性。宏定义是C语言中的一个重要特性,它允许程序员创建自定义的文本替换规则。本文将深入...

    c 语言中的宏,井号,可变参数.doc

    ### C 语言中的宏、井号与可变参数详解 #### 宏的概述与使用 在C语言中,宏(Macro)是一种预处理指令,它允许开发者定义一系列文本替换规则,这些规则会在编译之前由预处理器进行处理。宏不是在程序运行时执行的...

    用C语言实现参数个数可变的函数

    ANSI标准形式要求至少有一个命名的参数,并且函数原型中使用省略号表示可变参数的存在。而与UNIX System V兼容的形式则不需要提供任何命名参数,而是直接使用va_list类型作为参数列表的开始。 va_start宏在函数内...

    C语言中的宏定义

    - 在宏定义中使用等号(`=`)是错误的做法,例如: ```c #define N=100 /* 错误 */ ``` 这样定义会导致预处理器在遇到宏时将其替换为`=100`,而非期望的数值`100`。 - 在宏定义末尾添加分号也是错误的,例如: ...

    C语言39:宏定义应用举例.rar

    如果在表达式中使用这个宏,可能会导致预期之外的结果,因为宏展开不考虑语境。例如: ```c int i = 1; printf("%d\n", INC(i) + 1); // 可能输出2而不是3 ``` 这是因为`INC(i)`先被展开,然后才进行加法操作,而...

    简述C语言中system()函数与vfork()函数的使用方法

    C语言system()函数:执行shell命令 头文件: #include 定义函数: int system(const char * string); 函数说明:system()会调用fork()产生子进程, 由子进程来调用/bin/sh-c string 来...

    32位单片机C语言编程:基于PIC32.pdf

    3. **外设驱动开发**:针对PIC32提供的各种外设,如ADC、DAC、UART等,书中详细讲解了如何使用C语言来控制这些外设。 4. **高级应用开发**:如实现通信协议(如I2C、SPI)、图形用户界面等,这些都是实际项目中经常...

    C语言中计算正弦的相关函数总结

    C语言sin()函数:正弦函数 头文件:  #include <math.h> sin() 函数用来求给定值的正弦值,其原型为: double sin(double x); 【参数】给定的值(弧度)。 【返回值】返回-1 至1 之间的计算结果。 弧度...

Global site tag (gtag.js) - Google Analytics