我正在将 32 位应用程序转换为 64 位应用程序,我遇到的痛点之一是可变参数函数,它期望一个 long 但可能传递一个整数,例如由于 long 大小的 64 位更改为 64 位,参数被硬编码为 -1 而不是 -1L。拿这个示例代码:
#include <stdio.h>
#include <stdarg.h>
long varargsExample(int input, ...);
int main(int argc, char **argv)
{
varargsExample(5,
"TestInt", 0,
/* This will fail if read as a long */
"TestIntNegative", -1,
"TestLong", 0L,
"TestLongNegative", -1L,
NULL);
}
long varargsExample(int firstArg, ...)
{
va_list args;
char * name;
long nextValue;
va_start(args, firstArg);
while ((name = va_arg(args, char *)) != 0)
{
/* If the type is changed to read in an int instead of long this works */
nextValue = va_arg(args, long);
printf("Got [%s] with value [%ld]\n", name, nextValue);
}
va_end(args);
return 0;
}
使用 GCC 64 位编译时运行它会导致:
Got [TestInt] with value [0]
Got [TestIntNegative] with value [4294967295]
Got [TestLong] with value [0]
Got [TestLongNegative] with value [-1]
这是有道理的,因为我猜这被解释为:
0000 0000 0000 0000 0000 0000 0000 0000 1111 1111 1111 1111 1111 1111 1111 1111
所以填充额外的 32 位来表示 long,我们得到 2^32 - 1 而不是负数。然而,我想知道的是,如果我将 va_arg 读取更改为将值读取为 int,无论是否传递了 int 或 long,这似乎都有效,例如:
nextValue = va_arg(args, int);
这是一个恰巧起作用的 hack,还是 C 规范中的某些东西使它能够始终如一地工作?请注意,此应用程序在 Unix/Linux 和 Windows 上运行,Windows 上的 long 是 32 位,所以我不担心函数被传递的值不能用 32 位整数表示。我创建了一个基本单元测试,它通过 INT_MIN --> INT_MAX 将整数/长整数的混合传递给可变参数函数并读取为 va_arg(args, int) 并且它似乎可以工作(在 AIX、Solaris 和 RHEL 上测试),但我不确定这是否只是在这些平台上发生的未定义行为。
这里的正确解决方法是识别该函数的所有调用者,并确保它们在所有情况下都传递了一个 long,但是如果没有编译器支持来识别这些函数,这些函数的使用相当普遍/很难识别。如果有一个 GCC 扩展我可以利用它来指定自定义可变参数类型检查,类似于格式参数检查(sprintf、printf 等)所做的,我试图将其视为一种替代方案。
最佳答案
编译器执行not know可变参数函数从列表中获取哪些类型,因此它依赖于给定参数的类型。它执行 default argument promotions论据。
对于整数类型,它们基本上将“较小”的类型提升为 int
或 unsigned
,并传递 int
/unsigned
和“更大”类型不变。
获取参数时为your responsibility从可变参数中获取 正确 类型。其他任何事情都会调用未定义的行为。
因此,由于您不传递 long
,而是传递 int
,因此您必须获取 int
。如果两种类型具有相同的表示,则错误可能会被忽视(正如您所怀疑的那样)。
但是,另一种方式 'round 也不应该起作用:如果已推送较大的 long
,则采用较小的 int
。然而,对于典型的实现,只有在获取下一个参数时才会注意到这一点。无论哪种方式,由于这都是未定义的行为,因此必须避免。
gcc 对 printf
/scanf
-like 格式字符串使用函数 __attribute__
s 有一些支持,但作为函数的调用者不提供向调用者提示类型,您在编译器支持方面迷失了(它应该怎么知道?)。
像您提供的功能是骚乱程序的常见来源,最好避免,因为它们很容易出现排版错误,就像您现在注意到的一样。最好将结构数组传递给适当的函数或调用固定参数函数。它们通常是程序员为每一行代码而战的放射性遗产,无论是运行时还是大小。
另一种选择可能是 C11 使用带有 _Generic
的宏为各种参数类型调用固定大小的函数。
关于c - 可变参数函数传递很长但读取为 va_arg(argList, int),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32261736/