Как использовать Varargs в сочетании с указателями функций в C на Win64?
-
26-10-2019 - |
Вопрос
Рассмотрим следующую программу C:
#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);
}
По сути, идея состоит в том, чтобы сохранить функцию с переменными аргументами в «общем» указателе функции. Для сравнения, я также включил другую функцию с фиксированным списком аргументов. Теперь посмотрите, что происходит при запуске этого на x86 Linux, AMD64 Linux, Win32 и 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
Почему динамическая функция получает нулевое значение из списка аргументов переменной на Win64, но не ни на одной из других конфигураций? Что -то подобное даже законно? Я предположил, что это потому, что компилятор не жаловался.
Решение
Ваш код недопустим. Вызов вариальной функции требует прототипа, указывающего на то, что она вариадическая, а тип указателя функции, который вы используете, не предоставляет этого. Чтобы призыв не вызывать неопределенное поведение, вам придется отдать dynamic_func
указатель, как это, чтобы позвонить:
((void (*)(void *, ...))dynamic_func)(NULL, x);
Другие советы
Вы должны работать с последовательными определениями функций, даже если это означает использование Varargs, даже если не требуется. Лучшее - быть настолько многословным по мере необходимости.
...
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);
}