在 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

参见:STDARG documentation

您不能靠猜测来猜测 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 的结果。即使文档 对于平台文档可变参数的布局,事实上 编译器看不到它们被访问可能导致它得出结论 他们不是。