I'm trying to reconcile the rules I find for creating variadic functions in C. On the one hand I see explicitly stated (for example, here) statements like "just before the ellipses is always an int". On the other hand, I see lots of example programs, including on stackoverflow that make no mention of such a rule (or convention), and in fact work without it. And I see many of the other form (the extra int), that also seem to work. (The most common function, in fact seems to be one defined like: int myFunc(char *format, ...) and is used with sprintf or friends).
I'm trying to wrap my head around how it works so that future efforts are based upon understanding, rather than based on the use of copy/paste. At present, for me, it might as well be a magic wand. So in order to understand how to get the most out of the option, I need to understand the rules. Can you help me understand why I find such conflicting requirements and why both conventions seem to work?
Thanks.
The main rule regarding a variadic function is that you need some way of determining how many arguments you have and what the type of those arguments are, though not necessarily the way the tutorial say.
Generally, there are two ways: either one of the fixed arguments tells you the number and possibly the type of the variadic arguments, or one of the variadic arguments is a sentinel value which specifies the end of the argument list.
Examples from the standard library and POSIX:
printf and family: The first argument is a format string, and the contents of this format string specify the number and type of each variadic argument.
execl: The second of its two fixed arguments is the first argument of an external program to run. If it is not NULL, variadic arguments are read as type const char * until it finds one that is NULL.
A variation of the first option is as you mentioned: one of the fixed arguments is the number of variadic arguments, where each variadic argument has the same predetermined type. This is the simplest to implement, which is probably why the tutorial you linked suggested it.
Which of these you choose depends entirely on your use case.
Another interesting variation is the open function on Linux and similar systems. The man pages show the following signatures:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
The actual declaration looks something like this:
extern int open (const char *__file, int __oflag, ...) __nonnull ((1));
In this case, one variadic argument is read if the flags parameter includes the value O_CREAT.
There is no rule in the C standard that the parameter just before ... in a function declaration must be an int. The article you link to is merely referring to its particular example: When a function is declared with (int foo, ...), then the first argument passed to that specific function (after conversion from whatever the actual argument is; e.g., a char argument will be converted to int) is always an int.
In general, you can have any types for the parameters before .... The only rule is there must be at least one explicit parameter before the ....
Related
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.
Why is argc is given as a parameter in C (i.e. int main(int argc, char **argv)) when we actually do not pass the count of our arguments?
I want to know why the syntax is written in such a way when argc does not take the parameter passed. Why didn't they design it as a keyword or a function like length when it is written only for us to know the count?
You're right that when one of the exec*() family of functions is called, you do not specify the number of arguments explicitly — that is indicated by the presence of a null pointer at the end of a list of arguments.
The count is passed to the int main(int argc, char **argv) function for convenience, so that the code does not have to step through the entire argument list to determine how many arguments are present. It is only convenience — since argv[argc] == 0 is guaranteed, you can determine the end of the arguments unambiguously.
For the rest, the reason is historical — it was done that way from the start, and there has been no reason to change it (and every reason not to change it).
It isn't clear what you mean by 'a keyword' for the argument count. C has very few keywords, and one for this purpose would be peculiar. Similarly, although there could be a function to do the job, that isn't really necessary — the interface chosen obviates the need for such a function. It might have been useful to have functional access to the argument list (and the environment) so that library code could enumerate the arguments and environment. (Using getenv(), you can find out about environment variables you know about; you can't find out about environment variables which you don't know about. On POSIX systems, there is the extern char **environ; variable that be used to enumerate the content of the environment, but that's not part of Standard C.)
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.
Why doesn't C allow a function with variable length argument list such as:
void f(...)
{
// do something...
}
I think the motivation for the requirement that varargs functions must have a named parameter is for uniformity of va_start. For ease of implementation, va_start takes the name of the last named parameter. With a typical varargs calling convention, and depending on the direction arguments are stored, va_arg will find the first vararg at address (¶meter_name) + 1 or (first_vararg_type*)(¶meter_name) - 1, plus or minus some padding to ensure alignment.
I don't think there's any particular reason why the language couldn't support varargs functions with no named parameters. There would have to be an alternative form of va_start for use in such functions, that would have to get the first vararg directly from the stack pointer (or to be pedantic the frame pointer, which is in effect the value that the stack pointer had on function entry, since the code in the function might well have moved the sp since function entry). That's possible in principle -- any implementation should have access to the stack[*] somehow, at some level -- but it might be annoying for some implementers. Once you know the varargs calling convention you can generally implement the va_ macros without any other implementation-specific knowledge, and this would require also knowing how to get at the call arguments directly. I have implemented those varargs macros before, in an emulation layer, and it would have annoyed me.
Also, there's not a lot of practical use for a varargs function with no named parameters. There's no language feature for a varargs function to determine the type and number of variable arguments, so the callee has to know the type of the first vararg anyway in order to read it. So you might as well make it a named parameter with a type. In printf and friends the value of the first parameter tells the function what the types are of the varargs, and how many of them there are.
I suppose that in theory the callee could look at some global to figure out how to read the first argument (and whether there even is one), but that's pretty nasty. I would certainly not go out of my way to support that, and adding a new version of va_start with extra implementation burden is going out of my way.
[*] or if the implementation doesn't use a stack, to whatever it uses instead to pass function arguments.
With variable-length argument list you must declare the type of the first argument - that's the syntax of the language.
void f(int k, ...)
{
/* do something */
}
will work just fine. You then have to use va_list, va_start, va_end, etc. inside the function to access individual arguments.
C does allow for variable length arguments, but you need to use va_list, va_start, va_end, etc. for it. How do you think printf and friends are implemented? That said, I would recommend against it. You can usually accomplish a similar thing more cleanly using an array or struct for the parameters.
Playing around with it, made this nice implementation that I think some people might want to consider.
template<typename T>
void print(T first, ...)
{
va_list vl;
va_start(vl, first);
T temp = first;
do
{
cout << temp << endl;
}
while (temp = va_arg(vl, T));
va_end(vl);
}
It ensures you have one variable minimum, but allows you to put them all in a loop in a clean way.
There's no an intrisic reason why C can't accept void f(...). It could, but "designers" of this C feature decided not to do so.
My speculation about their motivations is that allowing void f(...) would require more "hidden" code (that can be accounted as a runtime) than not allowing it: in order to make distinguishable the case f() from f(arg) (and the others), C should provide a way to count how many args are given, and this needs more generated code (and likely a new keyword or a special variable like say "nargs" to retrieve the count), and C usually tries to be as minimalist as possible.
The ... allows for no arguments, ie: for int printf(const char *format, ...); the statement
printf("foobar\n");
is valid.
If you don't mandate at least 1 parameter (which should be used to check for more parameters), there is no way for the function to "know" how it was called.
All these statements would be valid
f();
f(1, 2, 3, 4, 5);
f("foobar\n");
f(qsort);
What the ... argument means in the declaration static void info(const char *fmt,...) ?
It's part of an C library I recently started to use. Sorry if it's basic C stuff but I never saw that before and google is not so verbose about ... !
It means variable arguments, which means the compiler will accept and compile calls to it with any arguments. Usually their types are indicated by values in preceeding arguments.
It takes a variable number of arguments in your method. I found this article explaining the details. It gets very complicated very quickly as you can see.
It is variable argument (Variadic function). It is just like printf.
int printf(const char *format, ...)
For more info, check this.
If a functions last argument is written as ... that means that the function takes arbitrarily many arguments (of arbitrary types as far as the compiler concerned - the function may of course require specific types, but the compiler has no way of enforcing those types).
These arguments can then be accessed using the va_* set of functions from stdarg.h.