【问题标题】:pointer to a va_list in amd64 ABI指向 amd64 ABI 中的 va_list 的指针
【发布时间】:2012-04-05 06:37:41
【问题描述】:

我担心 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 draft (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 使用方面存在(相同)问题的人的额外积分。

问候

【问题讨论】:

标签: c x86-64 abi variadic-functions


【解决方案1】:

行为完全符合,因为尽管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 的定义和类型在您的控制之下,&amp;ap2 具有正确的类型以传递给vptest

【讨论】:

  • 很难相信ap 在作为参数时没有va_list 类型。
  • 来自@michael-burr 的链接在这方面很有趣stackoverflow.com/a/8047513/611560
  • 以这种方式使用va_copy()可能依赖于未指定的行为(即,它是作为宏还是函数实现)...
  • @ydroneaud:如果你 typedef char foo[1]; 并使用 foo 作为参数类型,也会发生同样的事情。 jmp_buf 也会发生这种情况,但您期望这是因为 jmp_buf 被定义为数组类型。
  • @R..: va_copy 期望第二个参数的类型为va_list,如果ap 是类型调整的参数则不是这种情况; va_copy 的影响方式与原始代码相同;如指定的那样,stdarg.h 不会优雅地处理具有数组类型的va_list;我怀疑使用va_copy 将在实践中起作用(实现va_copy 的明显方法将适用于真实类型和调整后的类型),但要完全可移植,您需要一个配置时探针并有条件地在@987654350 上编译代码@;
猜你喜欢
  • 1970-01-01
  • 2011-08-04
  • 2016-12-28
  • 2021-08-14
  • 2019-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多