[C语言]变长函数参数和变长参数宏

C++里对于变化长参数已经有了较好的处理方案,这里对C语言的变长参数进行一个简答的总结。主要分为两块:函数和宏。

函数的变长参数

常见于printf系列函数,以printf的实现为例:

int printf (const char *format, ...) {
  va_list arg;
  int done;

  va_start (arg, format);
  done = vfprintf (stdout, format, arg);
  va_end (arg);

  return done;
}

主要用到了va_list系列宏,定义在stdarg.h头文件中。
注意,va_start是把操作指针移动到了变长参数头部,如果需要对变长参数操作多次,需要首先使用va_copy(dst,src)对变长参数进行备份。

宏的变长参数

宏的变长参数可用于定义一个自定义附加信息的assert:

#define vassert(expr, fmt, ...)                               \
    do {                                                      \
        if (!(expr)) {                                        \
            char *msg;                                        \
            asprintf(&msg, fmt, ##__VA_ARGS__);               \
            __assert_fail(msg, __FILE__, __LINE__, __func__); \
        }                                                     \
    } while (0)

这里用到的是__VA_ARGS__宏,C99中被标准化,编译时自动替换为实际对应的参数集。
##的作用是正确处理变长参数为空的情况。

题外话:

这里我们可以额外加入一个宏,使得vassert具有和assert一样的开关性质。

#ifdef NDEBUG
      #define vassert(expr, fmt, ...) ((void)0)
#else
      #define vassert(expr, fmt, ...) \
            ...
#endif

使用 #define vassert(expr, fmt, ...) ((void)0) 而非简单的 #define vassert(expr, fmt, ...) 或者 #define vassert(expr, fmt, ...) {},是因为这种写法能正常用于逗号表达式,或者条件语句,且能被编译器自动识别进行空语句优化。

// Comma expression
printf("test"), vassert(0, "fail");

// Conditional expression
test-expr ? perror("Hello") : vassert(0, "fail");
posted @ 2020-12-13 16:24  与MPI做斗争  阅读(836)  评论(0编辑  收藏  举报