I am reading http://www.cs.utexas.edu/users/lavender/courses/cs345/lectures/CS345-Lecture-07.pdf to try to understand how does Stack Activation Frame for Variable arguments functions works?
Specifically how can the called function knows how many arguments are being passed?
The slide said:
The va_start procedure computes the fp+offset value following the argument
past the last known argument (e.g., const char format). The rest of the arguments are then computed by calling
va_arg, where the ‘ap’ argument to va_arg is some fp+offset value.*
My question is what is fp (frame point)? how does va_start computes the 'fp+offset' values?
and how does va_arg get 'some fp+offset values? and what does va_end supposed to do with stack?
The function doesn't know how many arguments are passed. At least not in any way that matters, i.e. in C you cannot query for the number of arguments.
That's why all varargs functions must either:
Use a non-varying argument that contains information about the number and types of all variable arguments, like printf() does; or
Use a sentinel value on the end of the variable argument list. I'm not aware of a function in the standard library that does this, but see for instance GTK+'s gtk_list_store_set() function.
Both mechanisms are risky; if your printf() format string doesn't match the arguments, you're going to get undefined behavior. If there was a way for printf() to know the number of passed arguments, it would of course protect against this, but there isn't. So it can't.
The va_start() macro takes as an argument the last non-varying argument, so it can somehow (this is compiler internals, there's no single correct or standard answer, all we can do from this side of the interface is reason from the available data) use that to know where the first varying argument is located on the stack.
The va_arg() macro gets the type as an "argument", which makes it possible to use that to compute the offset, and probably increment some state in the va_list object to point at the next varying argument.
Related
In a function with variable arguments, we initialize an object of type va_list ,'ap' with the function va_start() as:
void va_start(va_list ap, parmN);
I don't understand
1.what type of objects can be passed as parMN(last known parameter). I've done with examples of passing integers, strings with format specifiers, structures etc.
2. How the parMN describes the following optional parameters.
The C standard says that va_start() is actually a macro, not a function, so it can do things a function couldn't. The details are highly implementation dependent but you could imagine that it takes the address of parmN to determine the stack address of the next parameter. There's no need for va_start() to know about the types of the following parameters because that information is passed to va_arg(), which is another macro.
According to what is written in the explanation in C - Variable Arguments:
Define a function with its last parameter as ellipses and the one just before the ellipses is always an int which will represent the number of arguments.
Is it really a must to send an int to the ellipse function before ...?
I saw the prototype of the printf() function, and before ... the function gets const char * and there isn't any int before the ....
No, passing an int is not a REQUIREMENT, as proved by printf(). What you see in the tutorial is specific to the func() and average() examples presented by the tutorial (although the func() example does not match the int explanation correctly, but the average() example does).
Only the caller knows how many parameter values it is passing in (and of what type(s)), so you need to design your variadic functions in such a way that the caller has to specify how many parameter values are actually being passed in (and optionally their types). There are two ways to do that:
passing in a required leading parameter. This could be an int specifying the exact argument count. Or it could be a string that the function parses to determine the arguments (this is what printf does).
passing in a required sentry value as the last argument value. Then the function simply uses all of the arguments it finds until it reaches that sentry.
I am currently changing the function signatures of a class of functions in an application. These functions are being stored in a function table, so I was expecting to change this function table as well. I have just realised that in certain instances, we already use the new function signature. But because everything is casted to the correct function type as it is put into the function table, no warnings are being raised.
When the function is called, it will be passed extra parameters that are not really part of the function declaration, but they are on the end of the parameter list.
I can't determine if this is guaranteed by the way function parameters are passed in C. I guess to do variadic functions like sprintf, it has to be the case that earlier arguments can be resolved correctly whatever is on the end of the parameter list?
It evidently works just fine across multiple platforms but out of curiosity I'd like to know how and why it works.
But because everything is casted to the correct function type as it is put into the function table, no warnings are being raised.
So the compiler gets to be no help to speak of. C programmers cast too much. >_<
I can't determine if this is guaranteed by the way function parameters are passed in C. I guess to do variadic functions like sprintf, it has to be the case that earlier arguments can be resolved correctly whatever is on the end of the parameter list?
Technically, you've got undefined behavior. But it's defined for your platform to use the standard C calling conventions (see Scott's answer), or something that maps directly to them (usually by mapping the first N parameters to a certain set of processor registers).
This comes up a lot with variable argument lists, too. For example, printf is declared something like:
int printf(const char* format, ...);
And its definition usually uses the stdarg system to handle the extra arguments, which looks like:
#include <stdarg.h>
int printf(const char* format, ...)
{
va_list ap;
int result;
va_start(ap, format);
result = vprintf(format, ap);
va_end(ap);
return result;
}
If you're on a platform with standard C calling conventions, that va_end(ap) macro usually turns into a do-nothing. In this case, you can get away with passing extra arguments to a function. But on some platforms, the va_end() call is required to restore the stack to a predictable state (i.e. where it was before the call to va_start); in those cases, your functions will not leave the stack the way it found it (it won't pop enough arguments back off the stack) so your calling function could, for example, crash on exit when it fetches a bogus value for a return address.
Your functions must certainly be using the cdecl calling convention (http://en.wikipedia.org/wiki/X86_calling_conventions#cdecl). This pushes arguments on the stack in reverse order, from right to left, ensuring that the last argument can be easily located (top of the stack) and used to interpret the remainder, such as a printf format string. It is also the responsibility of the caller to clean up the stack, which is a bit less compact than the function itself doing so (as in pascal/stdcall convention), but ensures that variable argument lists can be used, and implies that trailing arguments can be ignored.
Here I found an example of how varargs can be used in C.
#include <stdarg.h>
double average(int count, ...)
{
va_list ap;
int j;
double tot = 0;
va_start(ap, count); //Requires the last fixed parameter (to get the address)
for(j=0; j<count; j++)
tot+=va_arg(ap, double); //Requires the type to cast to. Increments ap to the next argument.
va_end(ap);
return tot/count;
}
I can understand this example only to some extent.
It is not clear to me why we use va_start(ap, count);. As far as I understand, in this way we set the iterator to its first element. But why it is not set to the beginning by default?
It is not clear to me why we need to give count as an argument. Can't C automatically determine the number of the arguments?
It is not clear to me why we use va_end(ap). What does it change? Does it set the iterator to the end of the list? But is it not set to the end of the list by the loop? Moreover, why do we need it? We do not use ap anymore; why do we want to change it?
Remember that arguments are passed on the stack. The va_start function contains the "magic" code to initialize the va_list with the correct stack pointer. It must be passed the last named argument in the function declaration or it will not work.
What va_arg does is use this saved stack pointer, and extract the correct amount of bytes for the type provided, and then modify ap so it points to the next argument on the stack.
In reality these functions (va_start, va_arg and va_end) are not actually functions, but implemented as preprocessor macros. The actual implementation also depends on the compiler, as different compilers can have different layout of the stack and how it pushes arguments on the stack.
But why it is not set to the beginning by default?
Maybe because of historical reasons from when compilers weren't smart enough. Maybe because you might have a varargs function prototype which doesn't actually care about the varargs and setting up varargs happens to be expensive on that particular system. Maybe because of more complex operations where you do va_copy or maybe you want to restart working with the arguments multiple times and call va_start multiple times.
The short version is: because the language standard says so.
Second, it is not clear to me why we need to give count as an argument. Can't C++ automatically determine the number of the arguments?
That's not what all that count is. It is the last named argument to the function. va_start needs it to figure out where the varargs are. Most likely this is for historical reasons on old compilers. I can't see why it couldn't be implemented differently today.
As the second part of your question: no, the compiler doesn't know how many arguments were sent to the function. It might not even be in the same compilation unit or even the same program and the compiler doesn't know how the function will be called. Imagine a library with a varargs function like printf. When you compile your libc the compiler doesn't know when and how programs will call printf. On most ABIs (ABI is the conventions for how functions are called, how arguments are passed, etc) there is no way to find out how many arguments a function call got. It's wasteful to include that information in a function call and it's almost never needed. So you need to have a way to tell the varargs function how many arguments it got. Accessing va_arg beyond the number of arguments that were actually passed is undefined behavior.
Then it is not clear to me why do we use va_end(ap). What does it change?
On most architectures va_end doesn't do anything relevant. But there are some architectures with complex argument passing semantics and va_start could even potentially malloc memory then you'd need va_end to free that memory.
The short version here is also: because the language standard says so.
va_start initalises the list of variable arguments. You always pass the last named function argument as the second parameter. It is because you need to provide information about location in the stack, where variable arguments begin, since arguments are pushed on the stack and compiler cannot know where there's a beginning of variable argument list (there's no differentiation).
As to va_end, it is used to free resources allocated for the variable argument list during the va_start call.
It's C macroses. va_start sets internal pointer to address of first element. va_end cleanup va_list. If there is va_start in code and there is no va_end - it's UB.
The restrictions that ISO C places on the second parameter to the va_start() macro in header
are different in this International Standard. The parameter parmN is the identifier of the rightmost parameter
in the variable parameter list of the function definition (the one just before the ...). If the parameter
parmN is declared with a function, array, or reference type, or with a type that is not compatible with the
type that results when passing an argument for which there is no parameter, the behavior is undefined.
I'm trying to plug a hole in my knowledge. Why variadic functions require at least two arguments? Mostly from C's main function having argc as argument count and then argv as array of arrays of chars? Also Objective-C's Cocoa has NSString methods that require format as first argument and afterwards an array of arguments ([NSString stringWithFormat:#"%#", foo]). Why is it impossible to create a variadic function accepting only a list of arguments?
argc/argv stuff is not really variadic.
Variadic functions (such as printf()) use arguments put on the stack, and don't require at least 2 arguments, but 1.
You have void foo(char const * fmt, ...) and usually fmt gives a clue about the number of arguments.
That's minimum 1 argument (fmt).
C has very limited reflection abilities so you must have some way to indicate what it is that the variable arguments contain - either specifying the number of arguments or the type of them (or both), and that is the logic behind having one more parameter. It is required by the ISO C standard so you can't omit it. If feel you don't need any extra parameters because the number and type of the arguments is always constant then there is no need for variable arguments in the first place.
You could of course design other ways to encode the number / type information inside the variable arguments such as a sentinel value. If you want to do this, you can just supply a dummy value for the first argument and not use it in the method body.
And just to be pedantic about your title, variadic functions only require one argument (not two). It's perfectly valid to make a call to a variadic function without providing any optional arguments:
printf("Hello world");
I think, that the reason is the following:
in the macro va_start(list, param); you specify the last fixed argument - it is needed to determine the address of the beginning of the variable arguments list on the stack.
How would you then know if the user provided any arguments?
There has to be some information to indicate this, and C in general wasn't designed to do behind-your-back data manipulation. So anything you need, it makes you pass explicitly.
I'm sure if you really wanted to you could try to enforce some scheme whereby the variadic function takes only a certain type of parameter (a list of ints for example) - and then you fill some global variable indicating how many ints you had passed.
Your two examples are not variadic functions. They are functions with two arguments, but they also highlight a similar issue. How can you know the size of a C array without additional information? You can either pass the size of the array, or you describe a scheme with some sentinel value demarcating the end of the array (i.e. '\0' for a C string).
In both the variadic case and the array case you have the same problem, how can you know how much data you have legitimate access to? If you don't know this with the array case, you will go out of bounds. If you don't know this with the variadic case you will call va_arg too many times, or with the wrong type.
To turn the question around, how would you be able to implement a function taking a variable number of arguments without passing the extra information?