4

Linux amd64(x86_64)での可変個引数関数について懸念があります。

私の例はLinuxi386(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へのポインターを作成し、そのポインターを別の関数に渡すことは許可されています。この場合、元の関数は、他の関数が戻った後、元のリストをさらに使用できます。

ここでAMD64ABIが標準と見なされて間違っているかどうかは、私にはわかりません。

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の動作が標準に一致している場合、誰かがどこかを見つけることができれば。stdargの使用に関する(同じ)問題を他のABIに提供してくれる人のための追加のポイント。

よろしく

4

1 に答える 1

3

vtestのように記述されている引数にもかかわらず、 には型がなく、むしろどのようなポインター型にも崩壊するため、動作は完全に準拠va_list apap てい va_listますva_list。これはva_list、標準で配列型であることを許可されているため、準拠しています。この問題の解決策は、ローカルva_copyにコピーするために使用することです:apva_list

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

の定義と型はap2ユーザーの制御下にあるため、&ap2に渡す正しい型を持っていますvptest

于 2012-03-20T19:21:36.097 に答える