I've been experimenting with 'dynamically calling functions' using the source code below. After successfully testing this code with testing_function only accepting the first two arguments, I added in a third and decided 'not to supply the argument' when I call the function. I've noticed that when I do this, the value of the third argument is not (necessarily) 0, but a 'random' value that which I do not know the origin of.
Questions follow:
Foreword to source code follows:
I am running using Linux, compiling/calling a linker with GCC 4.6.3, and receive no compilation/linking warnings/errors when utilizing this code. This code executes 'perfectly'. I call gcc like the following:
gcc -x c -ansi -o (output file) (input file, .c suffix)
Source code follows:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
/* Function for testing. */
int testing_function(char* something, char* somethingelse, int somethingadditional)
{
int alt_errno = errno;
if ((something != NULL)&&(somethingelse != NULL))
{
errno = 0;
if (fprintf(stdout, "testing_function(\"%s\", \"%s\", %d);\n", something, somethingelse, somethingadditional) <= 0)
{
if (errno != 0)
{
int alt_alt_errno = errno;
perror("fprintf(stdout, \"testing_function(\\\"%%s\\\", \\\"%%s\\\", %%d);\\n\", something, somethingelse, somethingadditional)");
errno = alt_errno;
return alt_alt_errno;
}
else
{
errno = ENOSYS;
perror("fprintf(stdout, \"testing_function(\\\"%%s\\\", \\\"%%s\\\", %%d);\\n\", something, somethingelse, somethingadditional)");
errno = alt_errno;
return ENOSYS;
}
}
else
{
errno = alt_errno;
return 0;
}
}
else
{
errno = ENOSYS;
perror("testing_function(char* something, char* somethingelse, int somethingadditional)");
errno = alt_errno;
return ENOSYS;
}
}
/* Main function. */
int main(int argc, char** argv)
{
int (*function)(char*, char*);
*(void**) (&function) = testing_function;
exit(function("Hello", "world!"));
}
Where are these values originating from?
Generally they will be memory or register garbage from previous operations.
Additionaly, how are arguments passed to functions?
It depends on the platform ABI; generally either in a designated set of registers or at fixed offsets from a "stack pointer".
Is it bad practice to not pass arguments?
Yes. It triggers "undefined behavior"; the compiler is entitled to crash your program the moment you do it, or worse.
Can one be prepared for additions to a function's arguments without recompiling code utilizing the function? (example: a dynamically loaded library's function gains an accepted argument but code utilizing the function isn't going to be recompiled).
No. Whenever you change the argument list of a C function that is part of a library ABI you must also change its name. (There are tricks you can pull to hide this in the source-level API, but they are all veneers over the fundamental tactic of changing the function's name.)
In C++ of course the changed argument list is a new overload, but that's implemented by the compiler changing the name for you.
Function parameters are passed depending on the C ABI used by the compiler. This can mean they are passed on the stack or in registers or in a combination of both. I believe that 32-bit Intel systems commonly pass in the stack while 64-bit Intel pass mostly in registers with the overflow going on the stack.
Where do the random values for unpassed arguments come from? They come from the register or stack position that should have held the value. The called function does not know that the argument wasn't passed so it pulls it anyway.
If all of the arguments are supposed to be on the stack this can lead to bad problems because the function will pull off more stack items than exist. In the worst case it will wipe out the function return address.
When using registers it isn't much of a problem except for the random value.
From the above information you should be able to gather that it isn't supported and you shouldn't do it and in general it won't work.
What will work is variable argument lists. For example, printf
does it. So does the open()
POSIX function. The open declaration looks like the following:
extern int open (__const char *__file, int __oflag, ...);
See the triple dot? That declares a variable argument list. It can contain 0 to any number of arguments. They are accessed using special functions. The only way to know how many arguments to expect is one of the previous arguments. In the case of open()
, the oflag
value. For printf()
the format string.
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