指向amd64 ABI中的va_list的指针

时间:2012-03-20 18:33:46

标签: c x86-64 abi variadic-functions

我对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 draftISO/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使用方面存在(相同)问题的人的附加要点。

此致

1 个答案:

答案 0 :(得分:3)

行为完全一致,因为尽管vtest的论点被写为va_list apap 没有类型 va_list但是而不是指针类型va_list衰变到什么。这符合,因为标准允许va_list为数组类型。此问题的解决方案是使用va_copyap复制到本地va_list

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

由于ap2的定义和类型由您控制,&ap2具有传递给vptest的正确类型。