Consider the following C program:
#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);
}
Basically, the idea is to store a function with variable arguments in a "generic" function pointer. As a comparison, I've also included another function with fixed argument list. Now see what happens when running this on x86 Linux, amd64 Linux, Win32 and 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
Why does the dynamic function get a zero value from the variable argument list on Win64, but not on any of the other configurations? Is something like this even legal? I assumed it is because the compiler didn't complain.
Pass-by-pointer means to pass a pointer argument in the calling function to the corresponding formal parameter of the called function. The called function can modify the value of the variable to which the pointer argument points. When you use pass-by-pointer, a copy of the pointer is passed to the function.
You call it with a va_list and a type, and it takes value pointed at by the va_list as a value of the given type, then increment the pointer by the size of that pointer. For example, va_arg(argp, int) will return (int) *argp , and increment the pointer, so argp += sizeof int .
You can use pointers to call functions and to pass functions as arguments to other functions. You cannot perform pointer arithmetic on pointers to functions. For z/OS® XL C/C++, use the __cdecl keyword to declare a pointer to a function as a C linkage.
A function pointer, also called a subroutine pointer or procedure pointer, is a pointer that points to a function. As opposed to referencing a data value, a function pointer points to executable code within memory.
Your code is not valid. Calling a variadic function requires a prototype indicating that it's variadic, and the function pointer type you're using does not provide this. In order for the call not to invoke undefined behavior, you would have to cast the dynamic_func
pointer like this to make the call:
((void (*)(void *, ...))dynamic_func)(NULL, x);
You should work with consistent function definitions, even if that means to use varargs even if not needed. The best is to be as verbose as needed.
...
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With