c语言宏定义,可变参数的使用

...在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合法,将不会产生编译错误

来自:http://blog.csdn.net/acs713/article/details/6891888

C语言宏,"\"与一个较长占多行的宏

宏定义中允许包含两行以上命令的情形,此时必须在最右边加上"\"且该行"\"后不能再有任何字符,连注释部分都不能有,下面的每行最后的一定要是"\","\"后面加一个空格都会报错,更不能跟注释。

             #define exchange(a,b) {\

             int t;\

             t=a;\

             a=b;\

             b=t;\ }

 

 

        定义多行宏:注意斜杠的使用,最后一行不能用斜杠.

 #define DECLARE_RTTI(thisClass, superClass)\
  virtual const char* GetClassName() const\ 
  {return #thisClass;}\
  static int isTypeOf(const char* type)\
  {\
   if(!strcmp(#thisClass, type)\
    return 1;\
   return superClass::isTypeOf(type);\
   return 0;\
  }\
  virtual int isA(const char* type)\
  {\
   return thisClass::isTypeOf(type);\
  }\
  static thisClass* SafeDownCast(DitkObject* o)\
  {\
   if(o&&o->isA(#thisClass))\
    return static_cast<thisClass*>(o);\
   return NULL;\
  }
C(和C++)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念)。下面对常遇到的宏的使用问题做了简单总结。
关于#和##
在C语言的宏中 ,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。比如下面代码中的宏:
#define WARN_IF(EXP) /
  do{ if (EXP) /
  fprintf(stderr, "Warning: " #EXP "/n"); } /
  while(0)
那么实际使用中会出现下面所示的替换过程:
WARN_IF (divider == 0);
  被替换为
do {
  if (divider == 0)
  fprintf(stderr, "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;

 

      ## 连接符号由两个井号组成,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串。但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元。

     #符是把传递过来的参数当成字符串进行替代。

      下面来看看它们是怎样工作的。这是MSDN上的一个例子。 假设程序中已经定义了这样一个带参数的宏:

         #define paster( n ) printf( "token" #n " = %d", token##n )

           同时又定义了一个整形变量: int token9 = 9;

          现在在主程序中以下面的方式调用这个宏: paster( 9 );

         那么在编译时,上面的这句话被扩展为: printf( "token" "9" " = %d", token9 );

        注意到在这个例子中,paster(9);中的这个”9”被原封不动的当成了一个字符串,与”token”连接在了一起,从而成为了token9。而#n也被”9”所替代。 可想而知,上面程序运行的结果就是在屏幕上打印出token9=9

 

        定义单行宏:主要有以下三种用法.

   1) 前加##或后加##,将标记作为一个合法的标识符的一部分.注意,不是字符串.多用于多行的宏定义中.例如:

       #define A(x)  T_##x
则 int A(1) = 10; //等效于int T_1 = 10;
#define A(x)  Tx##__
则 int A(1) = 10; //等效于int T1__ = 10;

    2) 前加#@,将标记转换为相应的字符,注意:仅对单一标记转换有效

        #define B(x) #@x
 则B(a)即’a’,B(1)即’1’.但B(abc)却不甚有效.

   3) 前加#,将标记转换为字符串.

 #define C(x) #x
 则C(1+1) 即 ”1+1”.
 
2007-12-24 21:09

C语言中可变参数的宏

    今天来说说宏。什么?宏也能可变参数?是的,你没有听错,带参数的宏和函数一样,同样支持可变参数。下面通过一个小程序加以说明。
#include
#include

#define OUTSCREEN(msg, ...) printf(msg,__VA_ARGS__)

int main(int argc, char* argv[])
{
     OUTSCREEN("Hello World!n%s", "__This is a MACRO!n");
     return 0;
}

     这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。

#define OUTSCREEN(msg, ...) printf(msg, __VA_ARGS__)

int main(int argc, char* argv[])
{
     OUTSCREEN("Hello World!n%s", "__This is a MACRO!n");
     return 0;
}

    假如我们将上面的代码稍作一下修改,变成下面的样子。

#define OUTSCREEN(msg, ...) printf(msg, __VA_ARGS__)

int main(int argc, char* argv[])
{
     OUTSCREEN("Hello World!");
     return 0;
}

    注意我仅仅是将main函数里的OUTSCREEN做了修改,这时可变参数的个数为0了。但是编译的时候gcc却报错:
In function `main':
error: parse error before ')' token


    什么原因导致出错呢?把宏展开一下看看,原来是","惹得祸。那么这种参数个数可以为0的宏要怎么写呢?C99的规范没有定义这个,gcc对此做了扩展。重新定义OUTSCREEN宏如下:
#define OUTSCREEN(msg, ...) printf(msg, ##__VA_ARGS__)
    当可变参数的个数为0时,这里的##起到把前面多余的","去掉,实际上变成了printf(msg),这样编译就能通过了。

    另外,__VA_ARGS__这个宏实在不利于记忆,gcc对此做了扩展,另一种可接受的定义方法为:
#define OUTSCREEN(msg, args...) printf(msg, ##args)

贴上代码,权当练习之用

#include <stdio.h>

//#define OUTSCREEN(msg, ...) printf(msg, __VA_ARGS__)
//#define OUTSCREEN(msg, ...) printf(msg, ##__VA_ARGS__)
#define OUTSCREEN(msg, args...) printf(msg, ##args)
int main(int argc, char* argv[])
{
OUTSCREEN("Hello World=====%s\n");
//OUTSCREEN("Hello World=====%s", "__This is an example\n");
//    OUTSCREEN("Hello World=====%s", "__This is an example\n", "77777777777");
     return 0;
}

来自:http://hi.baidu.com/zzzkkk666/item/b36e9ecd0352b2080ad93a1c

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言中,我们可以使用标准库中的 `<stdarg.h>` 头文件来定义可变参数函数。下面是一个示例代码: ```c #include <stdio.h> #include <stdarg.h> void printNumbers(int count, ...) { va_list args; // 声明一个va_list类型的变量 va_start(args, count); // 初始化va_list变量 for (int i = 0; i < count; i++) { int num = va_arg(args, int); // 从可变参数列表中获取参数 printf("%d ", num); } va_end(args); // 结束可变参数的获取 printf("\n"); } int main() { printNumbers(5, 1, 2, 3, 4, 5); // 调用可变参数函数 return 0; } ``` 在上述示例代码中,我们首先包含了 `<stdarg.h>` 头文件,然后定义了一个名为 `printNumbers` 的可变参数函数。函数的第一个参数 `count` 用于指定可变参数的数量。然后使用 `va_list` 声明了一个可变参数列表的变量 `args`。通过调用 `va_start` 宏,我们对 `args` 进行了初始化,该宏接受两个参数,第一个是 `args`,第二个是最后一个固定参数的前一个参数(即 `count`)。接下来,我们使用 `va_arg` 宏在循环中逐个获取可变参数的值,第一个参数是 `args`,第二个参数是要获取的参数的类型。最后,我们使用 `va_end` 宏结束可变参数的获取。 在 `main` 函数中,我们调用了 `printNumbers` 函数来测试可变参数使用。 需要注意的是,在C语言中,可变参数函数的实现是比较底层和繁琐的,需要开发者手动管理参数列表的获取和结束。因此,在实际开发中,如果可能的话,建议使用C++中提供的更加方便和安全的可变参数函数实现方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值