如何判断va_arg列表的结尾?

How to determine the end of va_arg list?

我有一个功能foo(char *n, ...); 我需要获取并使用所有可选的 char 参数。 我想到了使用

while(va_arg(argPtr, char) != NULL)
{
   ...
}

了解我何时到达列表末尾。那么,它会起作用吗,如果在函数调用中我会做 foo(n, 't', 'm', '$', NULL); ?

NULL 会被 va_arg 读取为字符吗? 或者也许有一种更常见的方法来确定列表的结尾,而不添加 NULL 作为最后一个参数?

对于使用 va_arg 确定给定调用传递的参数的数量或类型的函数,没有 直接 方法。

您特别建议的方法:

while(va_arg(argPtr, char) != NULL)

不正确。 va_arg(argPtr, char) 产生一个 char 类型的值,而 NULL 是一个空指针常量。 (NULL 通常定义为 0,比较等于空字符 '[=17=]',但您不能依赖它。)

任何可变参数函数都必须有一种方法供调用者指定参数的数量和类型。例如,*printf 函数通过(非可变参数)格式字符串执行此操作。 POSIX execl*() 函数需要一个 char* 参数序列;参数列表的末尾由调用者用 (char*)NULL 标记。其他方法也是可能的,但它们几乎都依赖于参数中 运行 时间给出的信息。 (您可以使用一些其他方法,例如全局变量。请不要。)

这给调用者增加了负担,以确保传递给函数的参数是一致的。函数本身无法确认这一点。不正确的调用,如 printf("%d\n", "hello")execlp("name", "arg1") 具有 未定义的行为

还有一点:您不能将 va_argchar 类型的参数一起使用。当您调用可变参数函数时,对应于 , ... 的参数被 提升 。类型小于 int 的整数参数被提升为 intunsigned int,类型 float 的参数被提升为 double。如果调用者传递类型为 char 的参数,该函数必须调用 va_arg(argPtr, int).

(在您不太可能 运行 的非常晦涩的情况下,char 可以提升为 unsigned int。只有在 char是无符号且 sizeof (int) == 1,这意味着一个字节至少是 16 位。)

基本思路可行。但是您以几乎肯定不会的方式填写了详细信息。

当您使用可变参数时,通常的隐式转换规则不适用。相反,默认参数提升 发生,这意味着任何小于 int/unsigned int 的整数类型都会转换为其中之一——这不是唯一的提升,但这里唯一相关的 - 这也意味着不会自动转换为您使用 va_arg.

指定的任何类型

这意味着:

  • 您永远不能使用 va_arg(..., char),因为不可能将 char 作为可变函数参数传递。
  • 您永远不能将 NULL 作为可变函数参数传递,因为它的具体类型在很大程度上依赖于实现。现实类型有 intlongvoid *,还有许多其他不太现实但同样有效的类型。

你可以改变

while(va_arg(argPtr, char) != NULL)

while(va_arg(argPtr, int) != 0)

和通话

foo(n, 't', 'm', '$', NULL);

foo(n, 't', 'm', '$', 0);

在这一点上晚了几年,但我有一些有趣的贡献。在这种情况下,我会使用预处理器。顺便说一下,警告变量类型不正确。

注意:将函数名称括起来将使宏忽略它 同时允许文件符号与 API 相同(宏用法除外)。

方法一:哨兵宏

这样屏蔽你的函数:

#define my_func(...) my_func(__VA_ARGS__, NULL);
void (my_func)(...)
{
   /* ... */
}

方法二:数组长度宏

将可变参数转换为 int[],以静态确定 sizeof 所述数组:

#define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0]))              
#define NARGS(...) ARRAY_LENGTH((int[])({ __VA_ARGS__ }))

类似mask,但在前面加一个count参数:

#define my_func(...) my_func(NARGS(__VA_ARGS__), __VA_ARGS__);
void (my_func)(int n, ...)
{
   /* ... */
}

数组宏的编译特定类型检查:

超出了问题的范围,但是因为我在 roll.

我一直使用 GCC,有时也会使用下面的。我相信 Clang 中有半兼容的特性,但我不确定。这使您可以确保传递的是实际数组而不是指针。

#define TYPE_COMPATABLE(x, y) __builtin_types_compatible_p(__typeof__(x), __typeof__(y))
#define CHOOSE_EXPR(b, x, y) __builtin_choose_expr((b), (x), (y))
#define IS_ARRAY(a) (!TYPE_COMPATABLE((a), &(a)[0]))
#define ARRAY_LENGTH(a)                           \
    ({                                            \
        _Static_assert(IS_ARRAY(a), "non-array"); \
        sizeof(a) / sizeof((a)[0]);               \
    })

#define NARGS(...) ARRAY_LENGTH((int[])({ __VA_ARGS__ }))

到时候可能就用C++了呵呵