[英]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_arg
与char
类型的参数一起使用。 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
窄的整数参数被提升为int
或unsigned 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 相同(宏用法除外)。
Mask your functions as so:像这样掩盖你的功能:
#define my_func(...) my_func(__VA_ARGS__, NULL);
void (my_func)(...)
{
/* ... */
}
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, ...)
{
/* ... */
}
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:这意味着:
va_arg(..., char)
, since it's impossible to pass a char
as a variadic function argument.您永远不能使用va_arg(..., char)
,因为不可能将char
作为可变参数函数参数传递。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.现实类型是int
、 long
、 void *
,还有许多其他不太现实但同样有效的类型。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.