Ich möchte einen Funktionszeiger auf eine Funktion erstellen, die eine Teilmenge von Fällen für eine Funktion behandelt, die eine Variablenparameterliste verwendet. Der Anwendungsfall ist das Umsetzen einer Funktion, die ...
zu einer Funktion führt, die eine bestimmte Liste von Parametern verwendet, so dass Sie mit variablen Parametern umgehen können, ohne sich mit va_list
und Freunden zu befassen.In C, ist es jemals sicher, einen Variadic-Funktionszeiger auf einen Funktionszeiger mit endlichen Argumenten zu werfen?
Im folgenden Beispiel-Code, bin ich eine Funktion mit einer Variablen Parameter auf eine Funktion mit einer fest codierten Parameterliste (und umgekehrt). Das funktioniert (oder funktioniert zufällig), aber ich weiß nicht, ob es ein Zufall ist, aufgrund der verwendeten Aufrufkonvention. (Ich habe es auf zwei verschiedenen x86_64-basierten Plattformen probiert.)
Ist dieses undefinierte Verhalten? Wenn ja, kann jemand ein Beispiel für eine Plattform geben, wo dies nicht funktioniert, oder eine Möglichkeit, mein Beispiel so zu optimieren, dass es bei einem komplexeren Anwendungsfall fehlschlägt?
bearbeiten: die Implikation zu begegnen, dass dieser Code mit komplexeren Argumente brechen würde, erweiterte ich mein Beispiel:
#include <stdio.h>
#include <stdarg.h>
struct crazy
{
float f;
double lf;
int d;
unsigned int ua[2];
char* s;
};
void function1(char* s, ...)
{
va_list ap;
struct crazy c;
va_start(ap, s);
c = va_arg(ap, struct crazy);
printf(s, c.s, c.f, c.lf, c.d, c.ua[0], c.ua[1]);
va_end(ap);
}
void function2(char* s, struct crazy c)
{
printf(s, c.s, c.f, c.lf, c.d, c.ua[0], c.ua[1]);
}
typedef void (*functionX_t)(char*, struct crazy);
typedef void (*functionY_t)(char*, ...);
int main(int argc, char* argv[])
{
struct crazy c =
{
.f = 3.14,
.lf = 3.1415,
.d = -42,
.ua = { 0, 42 },
.s = "this is crazy"
};
/* swap! */
functionX_t functionX = (functionX_t) function1;
functionY_t functionY = (functionY_t) function2;
function1("%s %f %lf %d %u %u\n", c);
function2("%s %f %lf %d %u %u\n", c);
functionX("%s %f %lf %d %u %u\n", c);
functionY("%s %f %lf %d %u %u\n", c);
return 0;
}
Es funktioniert immer noch. Kann jemand auf ein konkretes Beispiel hinweisen, wann dies scheitern würde?
$ gcc -Wall -g -o varargs -O9 varargs.c
$ ./varargs
this is crazy 3.140000 3.141500 -42 0 42
this is crazy 3.140000 3.141500 -42 0 42
this is crazy 3.140000 3.141500 -42 0 42
this is crazy 3.140000 3.141500 -42 0 42
„Ist das nicht definiertes Verhalten?“ Ja. Der Aufruf einer Funktion über einen inkompatiblen Funktionszeiger führt immer zu undefiniertem Verhalten. –
Es scheint mir (zumindest durch ein Beispiel), dass das Schieben von Parametern auf den Stapel konsistent erfolgt, unabhängig davon, ob ein variadischer Aufruf verwendet wird oder nicht. Obwohl es in der Tat "undefiniert" sein kann (oder vielleicht nur nicht erwähnt wird), ist das Verhalten überhaupt inkonsistent? – mpontillo
@Mike: Nicht-variadische Parameter werden wann immer möglich durch die CPU-Register geleitet (zumindest auf x86). Sie werden überhaupt nicht auf den Stapel geschoben. Dies ist ein Grund, warum es nicht funktioniert. – AnT