Das Argument va_list ist eigentlich keine va_list


8

Beim Versuch, diesen Code zu kompilieren

#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;
}

Der GCC-Compiler gibt eine Warnung initialization from incompatible pointer typein der barFunktion aus. Die identische Aussage ist in Ordnung foo.

Es scheint, dass der Typ eines Agumentes des Typs va_listnicht a ist va_list. Dies kann leicht mit einer statischen Behauptung wie getestet werden

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

in der barFunktion. Mit GCC _Static_assertschlägt das fehl. Das gleiche kann auch in C ++ mit declytpeund getestet werden std::is_same.

Ich möchte die Adresse des va_list vlArguments von nehmen barund es als Argument von übergeben bar_ptr, um zu denken, wie in diesem Thread beschrieben . Auf der anderen Seite ist es in Ordnung, bar_ptr(n, pvl)direkt von anzurufen mainund zu ersetzen bar(n, vl).

Gemäß der Fußnote 253 des C11 endgültigen Entwurf ,

Es ist zulässig, einen Zeiger auf a zu erstellen va_listund diesen Zeiger an eine andere Funktion zu übergeben

Warum ist dies nicht möglich, wenn va_listes als Argument der Funktion definiert ist und nicht im Funktionskörper?

Problemumgehung:

Auch wenn dies die Frage nicht beantwortet, besteht eine mögliche Problemumgehung darin, den Inhalt barmithilfe einer lokalen Kopie des mit va_copy erstellten Arguments zu ändern:

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
In Bezug auf das Standardzitat, das Sie haben: Sie übergeben keinen Zeiger auf a va_list.
Ein Programmierer

4
Es ist zulässig, einen Zeiger auf eine va_list zu erstellen und diesen Zeiger an eine andere Funktion zu übergeben. Sie übergeben keinen Zeiger an a va_list, sondern einen tatsächlichen va_list.
Andrew Henle

Ich weiß, ich komme an einem vorbei va_list. Angenommen, ich habe eine dritte Funktion void bar_ptr(va_list *pvl);, ich möchte einen Zeiger va_list vlauf diese Funktion übergeben. Ich werde die Frage bearbeiten, um sie anzugeben.
Giovanni Cerretani

1
Diese und diese Frage könnten hilfreich sein. Und es ist auch sehr wahrscheinlich, dass der Compiler eine spezielle Behandlung vonva_list
Einige Programmierer Typ

1
Der va_copyAnsatz, den Sie haben, ist die kanonische Lösung für dieses Problem. Ich habe einige frühere Fragen dazu, die dies im Grunde genommen abgeschlossen haben.
R .. GitHub STOP HELPING ICE

Antworten:


4

va_listDer Standard erlaubt es, ein Array zu sein, und oft ist es das auch. Das bedeutet, dass va_listin einem Funktionsargument ein Zeiger auf va_listdas interne erste Element eines beliebigen Elements angepasst wird.

Die seltsame Regel ( 7.16p3 ) bezüglich der Übergabe berücksichtigtva_list grundsätzlich die Möglichkeit, dass va_listes sich um einen Array-Typ oder einen regulären Typ handelt.

Ich persönlich wickle mich va_listein, structdamit ich mich nicht darum kümmern muss.

Wenn Sie dann Zeiger an ein solches übergeben struct va_list_wrapper, ist es im Grunde so, als hätten Sie Zeiger an ein solches übergeben va_list, und dann gilt Fußnote 253 , die Ihnen die Erlaubnis gibt, dass sowohl ein Angerufener als auch ein Anrufer dasselbe va_listüber einen solchen Zeiger manipulieren .

(Dasselbe gilt für jmp_bufund sigjmp_bufvon setjmp.h. Im Allgemeinen ist diese Art der Anpassung von Array zu Zeiger einer der Gründe, warum Array-typisierte typedefs am besten vermieden werden. Es schafft nur Verwirrung, IMO.)


2
Wenn va_listes sich um ein Array handelt, ist es irreführend, wenn der Standard sagt: „Das Objekt apkann als Argument an eine andere Funktion übergeben werden…“ (C 2018 7.16 3, apist ein Objekt vom Typ va_list).
Eric Postpischil

2
@EricPostpischil: Der Standard sagt viele irreführende Dinge und betrachtet sie als nicht-a-but / WONTFIX, solange die Absicht "jemandem" klar ist ... :-P
R .. GitHub STOP HELPING ICE

2

Eine andere Lösung (nur C11 +):

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

Erläuterung: Wenn vlein Typ vorhanden ist va_list, va_listhandelt es sich nicht um einen Array-Typ, und es ist in Ordnung, nur die Adresse zu verwenden, um va_list *darauf zu verweisen. Andernfalls muss es einen Array-Typ haben, und dann dürfen Sie einen Zeiger auf das erste Element des Arrays (welcher Typ auch immer) auf einen Zeiger auf das Array setzen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.