How can variadic functions like printf find out the number of arguments they got?
The amount of arguments obviously isn't passed as a (hidden) parameter (see a call to printf in asm example here).
What's the trick?
The trick is that you tell them somehow else. For printf
you have to supply a format string which even contains type information (which might be incorrect though). The way to supply this information is mainly user-contract and often error-prone.
As for calling conventions: Usually the arguments are pushed onto the stack from left to right and then the backjump address at last. The calling routine clears the stack. So there is no technical need for the called routine to know the number of parameters.
EDIT: In C++0x there is a safe way (even typesafe!) to call variadic functions!
Implicitly, from the format string. Note that stdarg.h doesn't contain any macros to retrieve the total "variable" number of arguments passed. This is also one of the reasons the C calling convention requires the caller to clean the stack, even though this increases code size.
This is the reason why arguments are pushed on reverse order on the C calling convention, e.g:
If you call:
printf("%s %s", foo, bar);
The stack ends up like:
...
+-------------------+
| bar |
+-------------------+
| foo |
+-------------------+
| "%s %s" |
+-------------------+
| return address |
+-------------------+
| old frame pointer | <- frame pointer
+-------------------+
...
Arguments are accesed indirectly using its offset from the frame pointer (the frame pointer can be omitted by smart compilers that know how to calculate things from the stack pointer). The first argument is always at a well-known address in this scheme, the function accesses as many arguments as its first arguments tell it to.
Try the following:
printf("%x %x %x %x %x %x\n");
This will dump part of the stack.
The AMD64 System V ABI (Linux, Mac OS X) does pass the number vector (SEE / AVX) varargs in
rax
, unlike IA-32. See also: Why is %eax zeroed before a call to printf?TODO why is this required? I think it is only for performance reasons, to avoid saving unneeded SSE registers to the "Register Save Area" mentioned in "3.5.7 Variable Argument Lists".
On the C level, there are also other techniques besides parsing the format string as mentioned by others. You could also:
pass a sentinel
(void *)0
to indicate the last argument like execl does.You will want to use the
sentinel
function attribute to help GCC enforce that at compile time: C warning Missing sentinel in function callpass it as an extra integer argument with the number of varargs
use the
format
function attribute to help GCC enforce format strings of known types likeprintf
orstrftime
来源:https://stackoverflow.com/questions/5272703/how-do-vararg-functions-find-out-the-number-of-arguments-in-machine-code