在 C 中使用 va_list 作为数组
Using va_list as an array in C
像数组一样读取 va_list 而不是使用 va_arg 函数是否安全且已定义行为?
前:
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);
}
同样的作业问题
例如:
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
的更多 in-depth 视图,请参阅
this answer.
编辑
几个小时前,我写了 解释了如何使用
va_*
-宏。看看那个。
不,这不安全 well-defined。 va_list
结构可以是任何东西(假设它是指向第一个参数的指针),并且参数可能会或可能不会连续存储在 "right order" 所指向的某些内存区域中。
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 的编译器可能会查看测试的定义,
请注意,它(显然)在第一个时忽略了它的可变参数
参数为零,因此得出结论,它不需要计算和
传递 someComplicatedComputation 的结果。即使文档
对于平台文档可变参数的布局,事实上
编译器看不到它们被访问可能导致它得出结论
他们不是。
像数组一样读取 va_list 而不是使用 va_arg 函数是否安全且已定义行为?
前:
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);
}
同样的作业问题 例如:
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
的更多 in-depth 视图,请参阅
this answer.
编辑
几个小时前,我写了 va_*
-宏。看看那个。
不,这不安全 well-defined。 va_list
结构可以是任何东西(假设它是指向第一个参数的指针),并且参数可能会或可能不会连续存储在 "right order" 所指向的某些内存区域中。
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 的编译器可能会查看测试的定义, 请注意,它(显然)在第一个时忽略了它的可变参数 参数为零,因此得出结论,它不需要计算和 传递 someComplicatedComputation 的结果。即使文档 对于平台文档可变参数的布局,事实上 编译器看不到它们被访问可能导致它得出结论 他们不是。