简体   繁体   English

如何确定 va_arg 列表的结尾?

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

I have a function foo(char *n, ...);我有一个 function foo(char *n, ...); I need to get and use all of optional char parameters.我需要获取并使用所有可选的char参数。 I had an idea to use我有一个想法使用

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

to understand when I reach the end of the list.了解我何时到达列表末尾。 So, will it work, if in function call I'll do foo(n, 't', 'm', '$', NULL);那么,它会工作吗,如果在 function 电话中我会做foo(n, 't', 'm', '$', NULL); ?

Will NULL be read as a char by va_arg? NULL会被 va_arg 读取为 char 吗? Or maybe there's a more common way to determine the end of list, without adding NULL as last parameter?或者也许有一种更常见的方法来确定列表的结尾,而不添加NULL作为最后一个参数?

There is no direct way for a function that uses va_arg to determine the number or type(s) of the arguments passed by a given call.对于使用va_arg来确定给定调用传递的参数的数量或类型的函数,没有直接的方法。

Your proposed method in particular:特别是您提出的方法:

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

is incorrect.不正确。 va_arg(argPtr, char) yields a value of type char , while NULL is a null pointer constant. va_arg(argPtr, char)产生一个char类型的值,而NULL是一个空指针常量。 ( NULL is commonly defined as 0 , which compares equal to the null character '\\0' , but you can't rely on that.) NULL通常定义为0 ,它相当于空字符'\\0' ,但你不能依赖它。)

Any variadic function must have a way for the caller to specify the number and types of arguments.任何可变参数函数都必须有一种方法供调用者指定参数的数量和类型。 The *printf functions, for example, do so via the (non-variadic) format string.例如, *printf函数通过(非可变参数)格式字符串执行此操作。 The POSIX execl*() functions expect a sequence of char* arguments; POSIX execl*()函数需要一系列char*参数; the end of the argument list is marked by the caller with (char*)NULL .参数列表的末尾由调用者用(char*)NULL标记。 Other methods are possible, but they almost all depend on information given at run time in the arguments.其他方法也是可能的,但它们几乎都依赖于运行时参数中给出的信息。 (You could use some other method, such as a global variable. Please don't.) (您可以使用其他方法,例如全局变量。请不要。)

This places a burden on the caller to ensure that the arguments passed to the function are consistent.这给调用者带来了负担,以确保传递给函数的参数是一致的。 The function itself has no way to confirm this.函数本身无法确认这一点。 Incorrect calls, like printf("%d\\n", "hello") or execlp("name", "arg1") have undefined behavior .不正确的调用,如printf("%d\\n", "hello")execlp("name", "arg1")具有未定义的行为

One more thing: you can't use va_arg with an argument of type char .还有一件事:您不能将va_argchar类型的参数一起使用。 When you call a variadic function, arguments corresponding to the , ... are promoted .当您调用可变参数函数时,对应于, ...参数会被提升 Integer arguments of types narrower than int are promoted to int or to unsigned int , and arguments of type float are promoted to double .类型比int窄的整数参数被提升为intunsigned int ,而float类型的参数被提升为double If a caller passes an argument of type char , the function must invoke va_arg(argPtr, int) .如果调用者传递char类型的参数,则该函数必须调用va_arg(argPtr, int)

(In very obscure circumstances that you're not likely to run into, char can be promoted to unsigned int . That can happen only if plain char is unsigned and sizeof (int) == 1 , which implies that a byte is at least 16 bits.) (在您不太可能遇到的非常模糊的情况下, char可以提升为unsigned int 。只有当普通char是 unsigned 并且sizeof (int) == 1才会发生这种情况,这意味着一个字节至少为 16位。)

Just a few years late at this point, but I have some interesting contributions.在这一点上只是晚了几年,但我有一些有趣的贡献。 In this case, I would use the pre-processor.在这种情况下,我会使用预处理器。 Be warned of incorrect types with variadics btw.顺便说一下,警告变量类型不正确。

Note: Parenthesizing the function name will make macros ignore it while allowing the file symbols to not be different than the API (other than macro usage).注意:用括号括起 function 名称将使宏忽略它,同时允许文件符号与 API 相同(宏用法除外)。

Method 1: Sentinel Macro方法一:哨兵宏

Mask your functions as so:像这样掩盖你的功能:

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

Method 2: Array Length Macro方法二:数组长度宏

Cast the variadic to a int[] , to statically determine sizeof said array:将可变参数转换为int[] ,以静态确定所述数组的sizeof

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

Similarly mask, but add a count argument in front:类似mask,只是在前面加一个count参数:

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

Compile-Specific Type Checking For the Array Macro:数组宏的编译特定类型检查:

Outside the scope of the question, but since I'm on a roll .在问题的 scope 之外,但由于我在滚动

I always use GCC, and sometimes utilize the following.我总是使用 GCC,有时使用以下。 I believe there are semi-compatible features in Clang, but I'm not sure.我相信 Clang 中有半兼容功能,但我不确定。 This allows you to be sure an actual array is being passed and not a pointer.这使您可以确保传递的是实际数组而不是指针。

#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__ }))

At that point, might just use C++ hehe.到时候,可能就用 C++ 呵呵。

The basic idea would work.基本的想法是可行的。 But you've filled in the details in a way that almost certainly won't.但是您以几乎肯定不会的方式填写了详细信息。

The usual implicit conversion rules don't apply when you're using variadic arguments.当您使用可变参数时,通常的隐式转换规则不适用。 Instead, default argument promotions take place, which means that any integer type smaller than int / unsigned int gets converted to one of those -- that's not the only promotion, but the only one relevant here -- and which also means that there is no automatic conversion to whatever type you specify with va_arg .相反,会发生默认参数提升,这意味着任何小于int / unsigned int整数类型都会被转换为其中之一——这不是唯一的提升,而是这里唯一相关的——这也意味着没有自动转换为您使用va_arg指定的任何类型。

This means that:这意味着:

  • You cannot ever use va_arg(..., char) , since it's impossible to pass a char as a variadic function argument.您永远不能使用va_arg(..., char) ,因为不可能将char作为可变参数函数参数传递。
  • You cannot ever pass NULL as a variadic function argument, since its concrete type is heavily implementation-dependent.您永远不能将NULL作为可变参数函数参数传递,因为它的具体类型在很大程度上依赖于实现。 Realistic types are int , long , void * , and there are loads of other less realistic but equally valid types.现实类型是intlongvoid * ,还有许多其他不太现实但同样有效的类型。

You could change你可以改变

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

to

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

and the call和电话

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

to

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

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

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