繁体   English   中英

如何确定 va_arg 列表的结尾?

[英]How to determine the end of va_arg list?

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

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

了解我何时到达列表末尾。 那么,它会工作吗,如果在 function 电话中我会做foo(n, 't', 'm', '$', NULL);

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

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

特别是您提出的方法:

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

不正确。 va_arg(argPtr, char)产生一个char类型的值,而NULL是一个空指针常量。 NULL通常定义为0 ,它相当于空字符'\\0' ,但你不能依赖它。)

任何可变参数函数都必须有一种方法供调用者指定参数的数量和类型。 例如, *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是 unsigned 并且sizeof (int) == 1才会发生这种情况,这意味着一个字节至少为 16位。)

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

注意:用括号括起 function 名称将使宏忽略它,同时允许文件符号与 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, ...)
{
   /* ... */
}

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

在问题的 scope 之外,但由于我在滚动

我总是使用 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++ 呵呵。

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

当您使用可变参数时,通常的隐式转换规则不适用。 相反,会发生默认参数提升,这意味着任何小于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);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM