我做了一个简单的测试用例:
static void va_test(char* str_arg, ...)
{
va_list ap;
va_start(ap, str_arg);
for( ; ; ) {
if (str_arg == NULL)
break;
int n = va_arg(ap,int);
printf("arg: %s,%d\n", str_arg, n);
str_arg = va_arg(ap,char*);
}
va_end(ap);
printf("\n");
}
当我在独立的可执行文件中使用va_test("beer",1,"cofe",2,"juice",3,0)
运行它时,它可以正常工作。但是当我从我的项目可执行文件中调用它时,它非常大,它会给出一些像这样的垃圾字符串:
arg: bear,1
arg: cofe,2
arg: juice,3
arg: ^X(garbage...),57
我想在调用此函数之前一定会出现内存混乱,但我该如何调试呢?
[编辑]
我稍微更新了一下描述,因为严格来说,当我将超过6个args传递给va_test时会发生错误。我意识到前六个64位args是通过amd64机器中的寄存器传递的,而其他args是通过堆栈传递的。当va_arg尝试从*overflow_arg_area
获取第一个arg时会发生此问题。
答案 0 :(得分:4)
最有可能的解释是,您所在的系统int
0的表示形式与char *
0不同。这可能位于64位系统sizeof(int) == 4
1}}和sizeof(char *) == 8
。
尝试将最后一个参数传递为(char *) 0
而不是0
,你应该没问题。所有其余代码看起来在技术上都是正确的。
答案 1 :(得分:2)
如果使用GCC,您可以使用va_test
声明__attribute__((sentinel))
并确保每个通话事件都以空值终止,例如。
#define va_test(Fmt,...) va_test(Fmt,__VA_ARGS__,NULL)
我猜你的内存混乱是因为某些调用不是空终止。
答案 2 :(得分:1)
来自va_arg
的Linux手册页:
If there is no next argument, or if type is not compatible with the type of the
actual next argument (aspromoted according to the default argument promotions),
random errors will occur.
你必须找到另一种结束循环的方法,一旦取出所有参数,你就不能相信va_arg
的结果。
您获得的随机值就是在最后一个参数之后的堆栈中的内容。
答案 3 :(得分:0)
添加到Starynkevitch和Pileborg,为了更好的可读性,一段时间内更换for循环可能也是一个好主意。
while (str_arg != NULL) {
/* Do stuff */
}
(但用新的结束条件替换str_arg != NULL
。)[编辑:谢谢Jens。]
答案 4 :(得分:0)
这是C标准不是很明确的情况。在可变参数函数以空值终止的情况下可能会出现问题。对于NULL
定义为(void *)0
的实现,指针作为参数传递,因此必须使用va_arg
读取指针以避免未定义的行为。如果NULL
被定义为0
(C标准允许但是我自己的个人经验不是一个流行的定义),那么int
作为参数传递,并且因此,必须使用int
读取va_arg
以避免未定义的行为。 int
和void *
是两种根本不同的类型,通常具有不同的大小,因此可能会导致实际问题。
如果您的C实现将NULL
定义为(void *)0
,请务必在函数调用中提供NULL
,而不是0
。如果你希望你的代码尽可能地可移植,我会避免传递0
或NULL
,而是提供一个空字符串作为终结符,或类似的东西。