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 type
in der bar
Funktion aus. Die identische Aussage ist in Ordnung foo
.
Es scheint, dass der Typ eines Agumentes des Typs va_list
nicht 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 bar
Funktion. Mit GCC _Static_assert
schlägt das fehl. Das gleiche kann auch in C ++ mit declytpe
und getestet werden std::is_same
.
Ich möchte die Adresse des va_list vl
Arguments von nehmen bar
und 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 main
und 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_list
und diesen Zeiger an eine andere Funktion zu übergeben
Warum ist dies nicht möglich, wenn va_list
es 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 bar
mithilfe 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);
}
quelle
va_list
.va_list
, sondern einen tatsächlichenva_list
.va_list
. Angenommen, ich habe eine dritte Funktionvoid bar_ptr(va_list *pvl);
, ich möchte einen Zeigerva_list vl
auf diese Funktion übergeben. Ich werde die Frage bearbeiten, um sie anzugeben.va_list
va_copy
Ansatz, 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.Antworten:
va_list
Der Standard erlaubt es, ein Array zu sein, und oft ist es das auch. Das bedeutet, dassva_list
in einem Funktionsargument ein Zeiger aufva_list
das interne erste Element eines beliebigen Elements angepasst wird.Die seltsame Regel ( 7.16p3 ) bezüglich der Übergabe berücksichtigt
va_list
grundsätzlich die Möglichkeit, dassva_list
es sich um einen Array-Typ oder einen regulären Typ handelt.Ich persönlich wickle mich
va_list
ein,struct
damit 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 übergebenva_list
, und dann gilt Fußnote 253 , die Ihnen die Erlaubnis gibt, dass sowohl ein Angerufener als auch ein Anrufer dasselbeva_list
über einen solchen Zeiger manipulieren .(Dasselbe gilt für
jmp_buf
undsigjmp_buf
vonsetjmp.h
. Im Allgemeinen ist diese Art der Anpassung von Array zu Zeiger einer der Gründe, warum Array-typisiertetypedef
s am besten vermieden werden. Es schafft nur Verwirrung, IMO.)quelle
va_list
es sich um ein Array handelt, ist es irreführend, wenn der Standard sagt: „Das Objektap
kann als Argument an eine andere Funktion übergeben werden…“ (C 2018 7.16 3,ap
ist ein Objekt vom Typva_list
).Eine andere Lösung (nur C11 +):
Erläuterung: Wenn
vl
ein Typ vorhanden istva_list
,va_list
handelt es sich nicht um einen Array-Typ, und es ist in Ordnung, nur die Adresse zu verwenden, umva_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.quelle