4

我担心 Linux amd64 (x86_64) 下的可变参数函数。

我的示例在 linux i386 (ia32) 上构建和工作正常,但是在为 linux amd64 构建时,GCC 会产生这样的错误:

stdarg.c: In function ‘vtest’:
stdarg.c:21:5: attention : passing argument 2 of ‘vptest’ from incompatible pointer type [enabled by default]
stdarg.c:5:1: note: expected ‘struct __va_list_tag (*)[1]’ but argument is of type ‘struct __va_list_tag **’

这里的例子:

#include <stdio.h>
#include <stdarg.h>

static int
vptest(int count, va_list *a)
{
  printf("%8s:   a = %p\n", __func__, a);
  printf("%8s:   %d: %d\n", __func__, count, va_arg(*a, int));
  return 0;
}

static int
vtest(int count, va_list ap)
{
  printf("%8s: &ap = %p\n", __func__, &ap);

  /* passing a pointer to ap allows ap to be used again in the calling function */
  for(; count > 1; count --) {
    vptest(count, &ap);
  }
  if (count) {
    printf("%8s:   %d: %d\n", __func__, count, va_arg(ap, int));
  }
  return 0;
}

static
int test(int count, ...)
{
  va_list ap;

  va_start(ap, count);
  printf("%8s: &ap = %p\n", __func__, &ap);

  /* after passing ap to subfunction, this function must not use ap again without calling va_start */
  vtest(count, ap);

  va_end(ap);

  return 0;
}

int
main(void)
{
  test(4,
       1, 2, 3, 4);

  return 0;
}

根据C11 草案( ISO/IEC 9899:2011 )

对象 ap 可以作为参数传递给另一个函数;如果该函数调用带有参数 ap 的 va_arg 宏,则调用函数中 ap 的值是不确定的,应在进一步引用 ap 之前将其传递给 va_end 宏。

但后者添加

允许创建指向 va_list 的指针并将该指针传递给另一个函数,在这种情况下,原始函数可以在其他函数返回后进一步使用原始列表。

我不清楚AMD 64 ABI是否在标准上是错误的。

将函数更改为vtest()在第一次调用时使用指针可以解决问题,但是让内部函数中不起作用的东西实际上在外部函数中起作用感觉是错误的。

@@ -12,16 +12,16 @@
 }

 static int
-vtest(int count, va_list ap)
+vtest(int count, va_list *a)
 {
-  printf("%8s: &ap = %p\n", __func__, &ap);
+  printf("%8s:   a = %p\n", __func__, a);

   /* passing a pointer to ap allows ap to be used again in the calling function */
   for(; count > 1; count --) {
-    vptest(count, &ap);
+    vptest(count, a);
   }
   if (count) {
-    printf("%8s:   %d: %d\n", __func__, count, va_arg(ap, int));
+    printf("%8s:   %d: %d\n", __func__, count, va_arg(*a, int));
   }

   return 0;
@@ -37,7 +37,7 @@
   printf("%8s: &ap = %p\n", __func__, &ap);

   /* after passing ap to subfunction, this function must not use ap again without calling va_start */
-  vtest(count, ap);
+  vtest(count, &ap);

   va_end(ap);

如果有人可以找到 AMD64 ABI 行为是否符合标准的地方。对于向我提供其他 ABI 与 stdarg 使用(相同)问题的人的附加点。

问候

4

1 回答 1

3

这种行为是完全一致的,因为尽管参数vtest写为va_list ap,ap 没有类型 va_list,而是任何指针类型va_list衰减到。这是符合标准的,因为va_list标准允许它是数组类型。这个问题的解决方案是使用va_copy复制ap到本地va_list

va_list ap2;
va_copy(ap2, ap);
// ...
vptest(count, &ap2);
// ...
va_end(ap2);

由于 的定义和类型ap2在您的控制之下,&ap2因此有正确的类型可以传递给vptest.

于 2012-03-20T19:21:36.097 回答