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);
}
Giovanni Cerretani
quelle
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.)

PSkocik
quelle
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.

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle