像数组一样读取va_list而不是使用va_arg函数是安全且定义的行为吗?
EX:
void func(int string_count, ...)
{
va_start(valist, string_count);
printf("First argument: %d\n", *((int*)valist));
printf("Second argument: %d\n", *(((int*)valist)+1));
va_end(valist);
}
分配EX的问题相同:
void func(int string_count, ...)
{
va_start(valist, string_count);
printf("Third argument: %d\n", *(((int*)valist)+2));
*((int*)valist+2)=33;
printf("New third argument: %d\n", *(((int*)valist)+2));
va_end(valist);
}
PS:这似乎适用于GCC
不,它不是,你不能假设任何东西,因为不同的库实现各不相同。访问值的唯一可移植方法是使用stdarg.h
中定义的宏来访问省略号。类型的大小很重要,否则你最终会读取车库,如果你读取的字节多于传递的字节数,那么你就有了未定义的行为。
所以,要获得一个值,你必须使用va_arg
。
你不能继续猜测va_list
是如何工作的,或者是关于特定的实现。 va_list
如何工作取决于ABI,架构,编译器等。如果你想更深入地了解va_list
,请参阅this answer。
编辑
几个小时前,我写了this answer解释如何使用va_*
宏。看看那个。
不,这不安全且定义明确。 va_list
结构可以是任何东西(你假设它是指向第一个参数的指针),并且参数可以或可以不在被指向的某个存储区中的“正确顺序”中连续存储。
Example of va_list implementation that doesn't work for your code - 在这个设置中,一些参数在寄存器而不是堆栈中传递,但va_arg
仍然必须找到它们。
如果实现的文档指定va_list可能以超出标准中给出的方式使用,则可以在该实现上以这种方式使用它们。即使在指定参数布局的平台上,尝试以其他方式使用参数也可能产生不可预测的后果。例如,在一个平台上,变量参数以相反的顺序被压入堆栈,如果要执行以下操作:
int test(int x, ...)
{
if (!x)
return *(int*)(4+(uintptr_t)&x); // Address of first argument after x
... some other code using va_list.
}
int test2(void)
{
return test(0, someComplicatedComputation);
}
处理test2的编译器可能会查看test的定义,注意它(显然)在第一个参数为零时忽略它的可变参数,从而得出结论它不需要计算并传递someComplicatedComputation的结果。即使平台的文档记录了可变参数的布局,编译器无法看到它们被访问的事实也可能导致它得出结论它们不是。