2011-09-05 18 views
5

Considere el siguiente programa C:¿Cómo usar varargs junto con punteros a función en C en Win64?

#include <stdio.h> 
#include <stdarg.h> 

typedef void (callptr)(); 

static void fixed(void *something, double val) 
{ 
    printf("%f\n", val); 
} 

static void dynamic(void *something, ...) 
{ 
    va_list args; 
    va_start(args, something); 
    double arg = va_arg(args, double); 
    printf("%f\n", arg); 
} 

int main() 
{ 
    double x = 1337.1337; 
    callptr *dynamic_func = (callptr *) &dynamic; 
    dynamic_func(NULL, x); 
    callptr *fixed_func = (callptr *) &fixed; 
    fixed_func(NULL, x); 

    printf("%f\n", x); 
} 

Básicamente, la idea es almacenar una función con argumentos variables en un puntero de función "genérico". Como una comparación, también he incluido otra función con una lista de argumentos fija. Ahora veamos qué sucede cuando se ejecuta esto en Linux x86, AMD64 Linux, Win32 y Win64:

$ gcc -m32 -o test test.c 
$ file test 
test: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped 
$ ./test 
1337.133700 
1337.133700 
1337.133700 

$ gcc -o test test.c 
$ file test 
test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped 
$ ./test 
1337.133700 
1337.133700 
1337.133700 

C:\>gcc -o test.exe test.c 
C:\>file test.exe 
test.exe: PE32 executable for MS Windows (console) Intel 80386 32-bit 
C:\>test.exe 
1337.133700 
1337.133700 
1337.133700 

C:\>x86_64-w64-mingw32-gcc -o test.exe test.c 
C:\>file test.exe 
test.exe: PE32+ executable for MS Windows (console) Mono/.Net assembly 
C:\>test.exe 
0.000000 
1337.133700 
1337.133700 

¿Por qué la función dinámica obtener un valor cero de la lista de argumentos variable en Win64, pero no en cualquiera de las otras configuraciones ? ¿Es algo así incluso legal? Supuse que era porque el compilador no se quejó.

+1

Estoy bastante seguro de que esto no es legal; los punteros a las funciones no son convertibles de esta manera. –

Respuesta

5

Su bacalao e no es válido Llamar a una función variadic requiere un prototipo que indique que es variable, y el tipo de puntero a función que está utilizando no proporciona esto. A fin de que la llamada no invocar un comportamiento indefinido, que tendría que emitir el puntero dynamic_func como este para hacer la llamada:

((void (*)(void *, ...))dynamic_func)(NULL, x); 
+0

Gracias especialmente por la explicación. Como mi proyecto realiza muchas de estas llamadas, ahora escribí un pequeño preprocesador para convertir los punteros de función en el tipo correcto y funciona :) – smf68

1

Debe trabajar con definiciones de funciones coherentes, incluso si eso significa usar varargs incluso si no es necesario. Lo mejor es ser lo más detallado posible.

...

typedef void myfunc_t(void *, ...); 

...

myfunc_t dynamic; 
void dynamic(void * something, ...) 
{ 

...

} 

...

int main() 
{ 
    double x = 1337.1337; 
    myfunc_t *callnow; 
    callnow = &dynamic; 
    callnow(NULL, x); 

    printf("%f\n", x); 
} 
Cuestiones relacionadas