va_list引数は実際にはva_listではありません


8

このコードをコンパイルしようとすると

#include <stdarg.h>

void bar_ptr(int n, va_list *pvl) {
    // do va_arg stuff here
}

void bar(int n, va_list vl) {
    va_list *pvl = &vl; // error here
    bar_ptr(n, pvl);
}

void foo(int n, ...) {
    va_list vl;
    va_list *pvl = &vl; // fine here
    va_start(vl, n);
    bar(n, vl);
    va_end(vl);
}

int main() {
    foo(3, 1, 2, 3);
    return 0;
}

GCCコンパイラinitialization from incompatible pointer typeは、bar関数に関する警告を出力します。同一のステートメントはで問題ありませんfoo

typeの引数の型va_listがでないようva_listです。これは次のような静的アサーションで簡単にテストできます

_Static_assert(sizeof(vl) == sizeof(va_list), "invalid type");

bar機能。GCCでは、_Static_assert失敗します。同じことはしてC ++にもテストすることができるdeclytpestd::is_same

このスレッドで説明されているように考えるために、のva_list vl引数のアドレスを取り、barそれをの引数として渡しbar_ptrたいと思います。一方、bar_ptr(n, pvl)から直接呼び出してmain、を置き換えても問題ありませんbar(n, vl)

C11最終草案の脚注253によると、

へのポインタを作成し、va_listそのポインタを別の関数に渡すことが許可されています

va_list関数本体ではなく、関数の引数として定義されている場合、これを実行できないのはなぜですか?

回避策:

これで問題が解決しない場合でも、回避策としてはbar、va_copyで作成した引数のローカルコピーを使用しての内容を変更します。

void bar(int n, va_list vl) {
    va_list vl_copy;
    va_copy(vl_copy, vl);
    va_list *pvl = &vl_copy; // now fine here
    bar_ptr(n, pvl);
    va_end(va_copy);
}

2
あなたが持っている標準の引用について:あなたは実際にへのポインタを渡しませんva_list
一部のプログラマー

4
va_listのポインタを作成し、他の関数へのポインタを渡すことが許可されている あなたが渡していないポインタをするva_listあなたは、実際に渡しています、va_list
Andrew Henle

私は渡すことを知っていva_listます。3番目の関数があるとします。その関数へのvoid bar_ptr(va_list *pvl);ポインターを渡しva_list vlます。質問を編集して指定します。
Giovanni Cerretani

1
これこの質問は役に立ちます。また、コンパイラーが特別な処理をしている可能性も非常に高いva_list
一部のプログラマー

1
va_copyあなたが持っているアプローチは、この問題に対する標準的な解決策です。基本的にこれを結論付ける過去の質問がいくつかあります。
R .. GitHub ICE HELPING ICE

回答:


4

va_list標準では配列であることが許可されており、多くの場合そうです。つまりva_list、関数の引数は、va_list内部の最初の要素が何であるかへのポインタに調整されます。

渡される方法に関する奇妙な規則(7.16p3)は、va_list基本的にva_list配列型または通常の型の可能性に対応しています。

私は個人的にラップva_listしているstructので、これに対処する必要はありません。

その後、そのようなにポインターを渡すとstruct va_list_wrapper、基本的にはにポインターを渡した場合と同様にva_list脚注253が適用され、呼び出し先と呼び出し元の両方に、va_listそのようなポインターを介して同じ操作を許可します。

(同じことがからjmp_bufもに適用さsigjmp_bufsetjmp.hます。一般に、このタイプの配列からポインターへの調整は、配列型typedefのが回避される理由の1つです。IMOは混乱を招くだけです。)


2
va_list配列である可能性がある場合、「オブジェクトapは引数として別の関数に渡される可能性があります…」と標準が誤解を招く可能性があります(C 2018 7.16 3 apは型のオブジェクトですva_list)。
Eric Postpischil

2
@EricPostpischil:意図が「誰か」に明確である限り、標準は多くの誤解を招くようなことを言ってそれらをWONTFIXではないと見なします... :-P
R .. GitHub ICE

2

別のソリューション(C11 +のみ):

_Generic(vl, va_list: &vl, default: (va_list *)vl)

説明:vltypeがある場合va_list、それva_listは配列型ではなく、アドレスを取得するだけでva_list *それを指すことができます。それ以外の場合は、配列型である必要があり、配列の最初の要素へのポインター(型が何であれ)を配列へのポインターにキャストできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.