Is it possible to make declaration of variadic function so that it doesn't end with "..."?
Today I learned more about exec from unistd.h but through the day I've seen three (two actually) different declaration of execl:
1) int execl ( const char * path, const char * arg0, ..., (char*)NULL ); was shown to us in school and I imagined I would have to end the function call with a NULL value
2) int execl(const char *path, const char *arg, ... /* (char *) NULL */); is what I've found in the exec(3) man page. That would probably mean I still have to end it with a NULL value, but it is not enforced.
3) int execl(const char *path, const char *arg, ...); is what I found here. This one would probably normally put me to rest with the first one being a simplification for students, the second was a varning and this is the real thing (even though I would probably have normally higher regard for both options one and two.)
But then I found on the same site this declaration:
int execle(const char *path, const char *arg, ..., char * const envp[]);
Same question applies, I was unable to create variadic function not ending in ... with gcc telling me that it's expecting ')' before ',' token pointing to the comma after the three dots.
So finally, is it possible to make variadic functions ending with a NULL characters (execl) and if not, is it possible to make it end with predefined variable (execle)?
I tried to compile with gcc 6.3.1, I also tried --std=c11.
Is it possible to make declaration of variadic function so that it doesn't end with "..."?
Is it possible is a slippery question, but consider these facts:
the standard says that "If a function that accepts a variable number of arguments is defined without a parameter type list that ends with the ellipsis notation, the behavior is undefined" (C2011, 6.9.1/8)
Perhaps that answers the question already, but if you choose to mince words and focus on function declarations that are not definitions, then
a function definition is also a declaration
the C language standard requires all declarations of the same function to be "compatible" (else program behavior is undefined) (C2011 6.7/4)
two function declarations with mismatched parameter lists are not compatible (C2011, 6.2.7/3)
Thus, if you declare a variadic function that in fact is also defined, and that function's parameter list does not end with ..., then the program's behavior is undefined.
The documentation you've been reading for execle() and execl() is written to express and discuss those functions' expectations, but to the extent that it seems to present variadic function declarations in which the last element of the parameter list is not ..., those are not actually valid C function declarations.
So finally, is it possible to make variadic functions ending with a NULL characters (execl) and if not, is it possible to make it end with predefined variable (execle)?
It is not possible to describe such calling conventions via conforming C declarations. Variadic functions can have such expectations, and can enforce them at runtime, but they can be enforced at compile time only by a compiler that relies on special knowledge of the functions involved, or on C language extensions that allow such constraints to be described.
The declaration of a variadic function can only specify the required arguments, and the compiler can enforce their types. The variable-length part never has any type checking done. And the variable-length part is always at the end. The declaration for execle() is not meant as an actual C declaration, but just to describe to the programmer how he should construct the arguments.
It's not possible to enforce that the last argument to execl() is NULL. Variadic functions don't know how many arguments were supplied, they determine it from the values of the arguments. printf() assumes that it has enough arguments to fill in all the operators in the format string, and execl() iterates through the arguments until it finds NULL (execle() is similar, but it reads one additional argument to get envp). If you don't end with NULL, it will just keep going, reading garbage and causing undefined behavior.
The declaration you see is the one in the man pages of execl. The declaration for execle in glib is the following: int execle (const char *path, const char *arg, ...). The implementation assumes the last argument is a char**, and uses it for envp. I don't think you can enforce such a rule in C.
Related
I'm using the below code:
char dest[5];
char src[5] = "test";
printf("String: %s\n", do_something(dest, src));
char *do_something(char *dest, const char *src)
{
return dest;
}
The implementation of do_something is not important here.
When I try to compile the above I get these two exception:
error: conflicting types for 'do_something' (at the printf call)
error: previous implicit declaration of 'do_something' was here (at the prototype line)
Why?
You are trying to call do_something before you declare it. You need to add a function prototype before your printf line:
char* do_something(char*, const char*);
Or you need to move the function definition above the printf line. You can't use a function before it is declared.
In "classic" C language (C89/90) when you call an undeclared function, C assumes that it returns an int and also attempts to derive the types of its parameters from the types of the actual arguments (no, it doesn't assume that it has no parameters, as someone suggested before).
In your specific example the compiler would look at do_something(dest, src) call and implicitly derive a declaration for do_something. The latter would look as follows
int do_something(char *, char *)
However, later in the code you explicitly declare do_something as
char *do_something(char *, const char *)
As you can see, these declarations are different from each other. This is what the compiler doesn't like.
A C Function-Declaration Backgrounder
In C, function declarations don't work like they do in other languages: The C compiler itself doesn't search backward and forward in the file to find the function's declaration from the place you call it, and it doesn't scan the file multiple times to figure out the relationships either: The compiler only scans forward in the file exactly once, from top to bottom. Connecting function calls to function declarations is part of the linker's job, and is only done after the file is compiled down to raw assembly instructions.
This means that as the compiler scans forward through the file, the very first time the compiler encounters the name of a function, one of two things have to be the case: It either is seeing the function declaration itself, in which case the compiler knows exactly what the function is and what types it takes as arguments and what types it returns — or it's a call to the function, and the compiler has to guess how the function will eventually be declared.
(There's a third option, where the name is used in a function prototype, but we'll ignore that for now, since if you're seeing this problem in the first place, you're probably not using prototypes.)
History Lesson
In the earliest days of C, the fact that the compiler had to guess types wasn't really an issue: All of the types were more-or-less the same — pretty much everything was either an int or a pointer, and they were the same size. (In fact, in B, the language that preceded C, there were no types at all; everything was just an int or pointer and its type was determined solely by how you used it!) So the compiler could safely guess the behavior of any function just based on the number of parameters that were passed: If you passed two parameters, the compiler would push two things onto the call stack, and presumably the callee would have two arguments declared, and that would all line up. If you passed only one parameter but the function expected two, it would still sort-of work, and the second argument would just be ignored/garbage. If you passed three parameters and the function expected two, it would also still sort-of work, and the third parameter would be ignored and stomped on by the function's local variables. (Some old C code still expects these mismatched-argument rules will work, too.)
But having the compiler let you pass anything to anything isn't really a good way to design a programming language. It worked well in the early days because the early C programmers were mostly wizards, and they knew not to pass the wrong type to functions, and even if they did get the types wrong, there were always tools like lint that could do deeper double-checking of your C code and warn you about such things.
Fast-forward to today, and we're not quite in the same boat. C has grown up, and a lot of people are programming in it who aren't wizards, and to accommodate them (and to accommodate everyone else who regularly used lint anyway), the compilers have taken on many of the abilities that were previously part of lint — especially the part where they check your code to ensure it's type-safe. Early C compilers would let you write int foo = "hello"; and it would just blithely assign the pointer to the integer, and it was up to you to make sure you weren't doing anything stupid. Modern C compilers complain loudly when you get your types wrong, and that's a good thing.
Type Conflicts
So what's all this got to do with the mysterious conflicting-type error on the line of the function declaration? As I said above, C compilers still have to either know or guess what a name means the first time they see that name as they scan forward through the file: They can know what it means it if it's an actual function declaration itself (or a function "prototype," more on that shortly), but if it's just a call to the function, they have to guess. And, sadly, the guess is often wrong.
When the compiler saw your call to do_something(), it looked at how it was invoked, and it concluded that do_something() would eventually be declared like this:
int do_something(char arg1[], char arg2[])
{
...
}
Why did it conclude that? Because that's how you called it! (Some C compilers may conclude that it was int do_something(int arg1, int arg2), or simply int do_something(...), both of which are even farther from what you want, but the important point is that regardless of how the compiler guesses the types, it guesses them differently from what your actual function uses.)
Later on, as the compiler scans forward in the file, it sees your actual declaration of char *do_something(char *, char *). That function declaration isn't even close to the declaration that the compiler guessed, which means that the line where the compiler compiled the call was compiled wrong, and the program is just not going to work. So it rightly prints an error telling you that your code isn't going to work as written.
You might be wondering, "Why does it assume I'm returning an int?" Well, it assumes that type because there's no information to the contrary: printf() can take in any type in its variable arguments, so without a better answer, int is as good a guess as any. (Many early C compilers always assumed int for every unspecified type, and assumed you meant ... for the arguments for every function declared f() — not void — which is why many modern code standards recommend always putting void in for the arguments if there really aren't supposed to be any.)
The Fix
There are two common fixes for the function-declaration error.
The first solution, which is recommended by many other answers here, is to put a prototype in the source code above the place where the function is first called. A prototype looks just like the function's declaration, but it has a semicolon where the body should be:
char *do_something(char *dest, const char *src);
By putting the prototype first, the compiler then knows what the function will eventually look like, so it doesn't have to guess. By convention, programmers often put prototypes at the top of the file, just under the #include statements, to ensure that they'll always be defined before any potential usages of them.
The other solution, which also shows up in some real-world code, is to simply reorder your functions so that the function declarations are always before anything that calls them! You could move the entire char *do_something(char *dest, const char *src) { ... } function above the first call to it, and the compiler then would know exactly what the function looks like and wouldn't have to guess.
In practice, most people use function prototypes, because you can also take function prototypes and move them into header (.h) files so that code in other .c files can call those functions. But either solution works, and many codebases use both.
C99 and C11
It is useful to note that the rules are slightly different in the newer versions of the C standard. In the earlier versions (C89 and K&R), the compiler really would guess the types at function-call time (and K&R-era compilers often wouldn't even warn you if they were wrong). C99 and C11 both require that the function declaration/prototype must precede the first call, and it's an error if it doesn't. But many modern C compilers — mainly for backward compatibility with earlier code — will only warn about a missing prototype and not consider it an error.
You didn't declare it before you used it.
You need something like
char *do_something(char *, const char *);
before the printf.
Example:
#include <stdio.h>
char *do_something(char *, const char *);
char dest[5];
char src[5] = "test";
int main ()
{
printf("String: %s\n", do_something(dest, src));
return 0;
}
char *do_something(char *dest, const char *src)
{
return dest;
}
Alternatively, you can put the whole do_something function before the printf.
You have to declare the function before you use it. If the function name appears before its declaration, C compiler will follow certain rules and makes the declaration itself. If it is wrong, you will get that error.
You have two options: (1) define it before you use it, or (2) use forward declaration without implementation. For example:
char *do_something(char *dest, const char *src);
Note the semicolon at the end.
C Commandment #3:
K&R #3 Thou shalt always prototype your functions or else the C compiler will extract vengence.
http://www.ee.ryerson.ca:8080/~elf/hack/God.vs.K+R.html
Watch again:
char dest[5];
char src[5] = "test";
printf("String: %s\n", do_something(dest, src));
Focus on this line:
printf("String: %s\n", do_something(dest, src));
You can clearly see that the do_something function is not declared!
If you look a little further,
printf("String: %s\n", do_something(dest, src));
char *do_something(char *dest, const char *src)
{
return dest;
}
you will see that you declare the function after you use it.
You will need to modify this part with this code:
char *do_something(char *dest, const char *src)
{
return dest;
}
printf("String: %s\n", do_something(dest, src));
Cheers ;)
When you don't give a prototype for the function before using it, C assumes that it takes any number of parameters and returns an int. So when you first try to use do_something, that's the type of function the compiler is looking for. Doing this should produce a warning about an "implicit function declaration".
So in your case, when you actually do declare the function later on, C doesn't allow function overloading, so it gets pissy because to it you've declared two functions with different prototypes but with the same name.
Short answer: declare the function before trying to use it.
This often happens when you modify a c function definition and forget to update the corresponding header definition.
Make sure that types in the function declaration are declared first.
/* start of the header file */
.
.
.
struct intr_frame{...}; //must be first!
.
.
.
void kill (struct intr_frame *);
.
.
.
/* end of the header file */
#include <arpa/inet.h>
for objective-c Conflicting types for 'inet_ntoa'
I want to create a function in C with variable length arguments in which I may not want to pass any variable at some point. I such a thing possible in C? I would like some code snippets if possible. Also, I have to mention that the function will have passed only char* variables when there are passed some variables.
A function with variable arguments must have at least one names argument in order to read the others.
For your particular case, since you want to pass a list of char * arguments you can use NULL to denote the end of the argument list. So in the case where there is nothing to pass, you just pass a singe NULL argument.
This is not supported by standard C.
For a function parameter type list terminated with an ellipsis, there must be at least one named parameter.
For a function defined with an identifier list, the number of arguments in the call must match the number of parameters in the definition; varying numbers of arguments are not supported.
If you will call a function with a varying number of char * arguments, and that number may be zero, you could declare the function with a dummy first argument that is always passed but never used, as with:
void foo(int dummy,...);
Note that passing no arguments is a troublesome design. Normally, routines that accept varying numbers of arguments infer the number of arguments from one or more arguments. For example, the first argument might contain the number of remaining arguments, or it might be a format string (as with printf) from which the number and types of remaining arguments can be deduced. If you want a function to accept zero arguments in some situations, it has to have a way of knowing whether it was called with zero arguments or more. That would require information in some global state, which is typically a bad design.
no you cant
you need at least one known argument
void foo(int dummy, ...)
{
va_list ap;
C does not provide any mechanism to allow a called function to determine how many arguments were provided. The mechanism must be part of the calling interface.
For example, printf relies on the % format specifications, while execv requires the caller to place a null pointer as the last argument.
A varargs function can be called with no variable arguments, but the caller must not attempt to access any variable argument in that case. For example, the mode argument to the Posix open() function is only consulted in the case that a new file is created, so it need not be supplied if the flags argument doesn't contain the O_CREAT flag (or some other extension flag, such as O_TMPFILE in Linux, which requests file creation).
The variable-length part of the prototype must follow at least one fixed argument. So it is not possible fir a varargs function to be called with no arguments at all.
Can you have in C variable length arguments functions with cases when you don't want to pass any variable?
Yes. In modern C, Code cannot define a ... function with no arguments, yet you can call such a function with no arguments by declaring the a function without function signature. In that way the function can be called with zero arguments.
The function still needs some way to understand the count of arguments. This is often done with the first argument as a count (or format) or a final argument as a sentinel.
Given "will have passed only char*"...
int foo_count(int counter, ...);
// zero usage example
foo_count(0);
int foo_sentinel(char *s);
// zero usage example
foo_sentinel((char*) NULL);
To do so with passing no arguments, the count needs to be conveyed in some other fashion, perhaps with a global variable - although this is not recommended. (Better to simply pass the count.)
Declare function:
int foo_count;
int foo(); // no prototype
Call function
foo_count = 0; foo()
foo_count = 1; foo("one")
foo_count = 2; foo("one", "two")
Define the function
int foo(char *s1, ...) {
if (foo_count > 0) {
va_list ap;
va_start(ap, s1);
puts(s1);
for (int i=1; i < foo_count; i++) {
puts(va_arg(ap, char *));
}
va_end(ap);
}
}
I'm currently reading a beginner book about embedded systems "Making embedded Systems - design patterns for great software". In the testing section they say its a good idea to have a terminal interface to your embedded system, with a set of commands you can call, to test certain things in your system.
The way they suggest implementing these commands is by having an array of c structures comprised of a function pointer and a const char *. Then initialise your command array with your command names and respective functions you want to be called by that command.
You have the user select type a string, that string then gets compared to the char * in your command array and if it matches a particular entry call the corresponding function in the structure.
Here's the example code.
typedef void(*functionPointerType)(void);
struct commandStruct
{
char const *name;
functionPointerType execute;
char const *help
};
const struct commandStruct commands[] = {
{"ver", &CmdVersion, "Display firmware version"},
{"flashTest" &CmdFlashTest, "Runs the flash unit test"}
};
I understand this fine. What I don't understand is the throw away comment afterwards which says, if one wanted to pass arguments to the functions having one would parse them from the command string and pass them to the function defined by the function pointer.
I was at first confused because I didn't think that C would allow me to assign a function which takes an argument to a function pointer that expects void, but I tried it and I can my it compiles and runs. The compiler does give me warnings though.
I guess my question is this: Is this a perfectly valid thing to do or is it a bit of a "hack"? Will certain compilers not allow me to do this?
Well, first of all, you're not assigning a function to a function pointer, but a pointer to a function to an object that holds a pointer to a function. And you don't need an & either, a function name decays to a pointer to a function almost everywhere:
const struct commandStruct commands[] = {
{"ver", (functionPointerType)CmdVersion, "Display firmware version"},
{"flashTest" (functionPointerType)CmdFlashTest, "Runs the flash unit test"}
};
This should silence the warnings, though some newer versions of GCC have gotten some really annoying warnings that will be enabled with -Wall that will complain about this perfectly valid construct.
Note that when you call the function via this pointer, you must cast it back to the original prototype, otherwise the behaviour will be undefined.
Therefore if you want to pass in arguments, you'd better change functionPointerType so that it matches the prototype of both CmdVersion and CmdFlashTest; as an added bonus you'd not need to have those explicit casts any more.
Two function types are not "compatible" with each other if their return types differ. If both are declared with prototypes, then they also are not compatible if they take different numbers of arguments or if any of the pairs of corresponding arguments fail to have compatible types.
It is allowed to convert between pointers to incompatible function types, and some compilers might even perform such conversions automatically, without a warning, though that would constitute an extension. But you elicit undefined behavior if you call a function via a pointer to a function type incompatible with the function's actual type, or if you pass arguments that (after argument promotions and conversions, as applicable), are not compatible with the function's declared parameter types.
Thus, your book's throwaway comment at best falls a bit short.
If you want to provide a terminal interface that allows commands to take arguments then there are a couple of different ways to approach it, but my first suggestion would be to emulate the signature expected for main() in a hosted environment. That is, declare the handler functions with this signature, instead:
typedef void(*functionPointerType)(int argc, char *argv[]);
There might be reason to assert varying kinds of constness on argv. That puts fairly minimal parsing requirements on the front end of the terminal interface, while giving all your handler functions a consistent signature that can accommodate arguments.
It is poor advice. Here's how it is done in practice.
Define your callback functions similar to main(), i.e. taking the number of strings, and the strings, as arguments: int callback(int argc, char *argv[]).
So, your command list could be e.g.
static const struct {
const char *name;
const char *help;
int (*func)(int argc, char *argv[]);
} firmware_cmd[] = {
{ "help", "help [ command ]", help_func },
{ "ver", "ver", display_version },
{ "flashtest", "flashtest", flash_text },
{ 0 }
};
In the firmware command parser/lexer, define some specific callback function return values:
enum {
FIRMWARE_CMD_OK = 0,
FIRMWARE_CMD_ARGS, /* Invalid arguments! */
FIRMWARE_CMD_HELP, /* Command help asked */
/* All others are error/failure codes */
};
Now, when the command parser/lexer calls the function, it outputs additional text depending on the return value:
FIRMWARE_CMD_OK: "OK"
FIRMWARE_CMD_ARGS: "Invalid arguments. Run 'help COMMAND' to see help, or 'help' to see full command list."
FIRMWARE_CMD_HELP: the ->help text.
All other return values: Error (returnvalue)
This should allow simple, but versatile firmware command functions, with additional detail (in the error number) if an error fails.
if one wanted to pass arguments to the functions [...] one would parse them from the command string and pass them to the function defined by the function pointer
This means you would change the function pointer type to one taking arguments, not you would assign a function of a different type to the pointer.
Eg.
typedef void (*functionType)(int argc, const char *argv[]);
I was at first confused because I didn't think that C would allow me to assign a function which takes an argument to a function pointer that expects void, but I tried it and I can my it compiles and runs. The compiler does give me warnings though.
If your program doesn't compile with -Wall -Werror (for GCC-style options, at least), it probably isn't correct.
Compiling with warnings just means the compiler kept going as best it could - C compilers generally lets you do things that aren't strictly legal, on the grounds that you might know better.
Is this a perfectly valid thing to do ... ?
No. It's a hack that could work correctly in certain - different - circumstances. Specifically, if you cast the function pointer back to the right type (matching the function prototype) before calling, it will probably work. This only makes sense if you somehow know (or record) what the correct type is.
The standard does not allow this, as the types are not compatible.
For a function to match the type of a function pointer, number and types of the arguments, as well as the return type, have to match. Otherwise, the function will not be called correctly and you invoke undefined behavior.
When you have a set of functions that can be called through a function pointer, all of those functions need to have the same signature. One way to do this is to have all such functions accept a single parameter of type void *, similarly to functions that start a thread. That way you can use a struct to contain all the arguments in question and pass its address to the function, then the function would cast the void * back to the expected type.
I wrote a function that that takes some argument and a pointer argument . when calling the function , sometimes I need to pass along the pointer for use inside the function and sometimes I don't .
what are the consequences of calling a function with fewer arguments ? it compiles correctly and during runtime its still fine , but is this good programming ? is it better if I call the function with a dummy variable ?
Thanks and sorry for beginner question .
If you call a function with too few arguments and the compiler doesn't complain, then you're doing something wrong.
You can write a function declaration/definition that doesn't specify how many arguments it requires:
void func();
/* ... */
func();
func(arg1);
func(arg1, arg2);
All three of those calls will be accepted by the compiler, but at least two of them are incorrect.
That form of function declaration/definition has been obsolescent since the 1989 ANSI C standard.
Never use this form.
Functions declaration should always be written as prototypes, i.e., declarations that specify the number and type(s) of the parameters. As a special case, (void) denotes a function with no parameters.
void func(int arg);
/* ... */
func(); /* rejected by compiler */
func(arg1); /* accepted -- but only if arg1 is of type int or convertible to int */
func(arg1, arg2); /* rejected by compiler */
If you manage to write code that calls a function with an incorrect number of arguments and get it past the compiler, the behavior is undefined. It might appear to "work", but it could blow up in your face when, for example, you compile it with a different compiler, or with the same compiler and different options.
One complication: some functions are variadic, taking a variable number of arguments. The most common example of this is printf. For variadic functions, the required arguments are typically specified by the function's documentation -- and it's just as important to get the arguments right. The difference is that, for variadic functions, your compiler won't necessarily tell you that a call is incorrect.
The , ... syntax (in the function declaration) and the macros defined in <stdarg.h> are the only legitimate way to write and use C functions that take a variable number and type(s) of arguments.
One of the differences of C++ over plain C is that it incorporates name mangling, which lets you specify a single function with varying return types and parameters. Two functions that share the same name but have different parameters:
int foo();
int foo(int param1, char* param2);
can be done in C++ because it actually alters the name of functions behind-the scenes as it compiles it. When you want to pass in a different number of parameters, it's essentially the same as having two different function names that it calls:
int foo1();
int foo2(int param1, char* param2);
When you pass in fewer parameters than it expects, some compilers should at least throw warnings; others won't even compile the program at all.
Let's say that you pass in 2 parameters to a function that expects 3. When you try to reference that 3rd parameter within your function, what do you expect the value to be? Most of the time, it will be a garbage value, and definitely something your function does not expect.
I would suggest just passing in a dummy value to functions like that: a NULL for a pointer or a 0 or negative "uninitialized" kind of value for other types. You can at least test for those values in your function. Or just write a second function that takes a different number of parameters.
I would not do this. I would only call the function with the declared number of arguments.
It is extremely confusing to the reader of the code.
The reader of the code might even think the function has been overloaded and look
for the other definitions of the function with fewer arguments.
The omitted arguments can have any value at all - anything is considered legal behavior by the C compiler. The program may work perfectly in a test environment and then crash for any or no reason.
If I absolutely had to have a variable number of arguments, for a legitimate purpose, I would try to put the arguments in a list instead of using a variable number of arguments. If someone else required me to use variable arguments, I would use varargs instead of the default behavior, and then only in the case where all the arguments are the same type.
I read here that in C void foo() means a function foo taking an unspecified number of arguments of unspecified type.
Can anyone give me or point me to an example where a C function takes an unspecified number of arguments? What can this be applied to in C? I couldn't find anything on the web.
That's an old-style function declaration.
This declaration:
void foo();
declares that foo is a function returning void that takes an unspecified but fixed number and type(s) of arguments. It doesn't mean that calls with arbitrary arguments are valid; it means that the compiler can't diagnose incorrect calls with the wrong number or type of arguments.
Somewhere, perhaps in another translation unit (source file), there has to be a definition of the function, perhaps:
void foo(x, y)
long x;
double *y;
{
/* ... */
}
This means that any call to foo that doesn't pass two arguments of type long and double* is invalid, and has undefined behavior.
Prior to the 1989 ANSI C standard, these were the only kind of function declaration and definition available in the language, and the burden of writing correct function calls was entirely on the programmer. ANSI C added prototypes, function declarations that specify the types of a function's parameters, which allow compile-time checking of function calls. (This feature was borrowed from early C++.) The modern equivalent of the above would be:
void foo(long x, double *y);
/* ... */
void foo(long x, double *y) {
/* ... */
}
Old-style (non-prototype) declarations and definitions are still legal, but they're officially obsolescent, which means that, in principle, they could be removed from a future version of the language -- though since they're still around in the 2011 standard I don't know that that will ever actually happen.
There is no good reason to use old-style function declarations and definitions in modern C code. (I've seen arguments for using them in some corner cases, but I find them unconvincing.)
C also supports variadic functions like printf, which do take an arbitrary number of arguments, but that's a distinct feature. A variadic function must be declared with a prototype, which includes a trailing , .... (Calling a variadic function with no visible prototype isn't illegal, but it has undefined behavior.) The function itself uses macros defined in <stdarg.h> to process its parameters. As with old-style function declarations, there is no compile-time checking for arguments corresponding to the , ... (though some compilers may check some calls; for example gcc warns if the arguments in a printf call are inconsistent with the format string).
This literally means that you are not telling the compiler what arguments the function takes, this means that it will not protect you from calling it with any arbitrary set of arguments. You would need to say in the definition precisely what arguments are actually taken for the function to be implemented however.
You could for example use this if you were generating a header file to describe a foreign function in external code, however you did not know what the function's signature actually was, it would still then be callable using your header but if you provide the wrong arguments in the call results are undefined.
The declaration void foo(); is not necessarily the same thing as a function that takes a variable number of arguments.
All that it is is a function declaration that indicates nothing about the number of arguments the function takes (actually, this is not exactly true, see below about argument promotions). The declaration isn't a function prototype, which provides information about the number and types of the parameters (using an ellipsis to indicate variable length parameters).
The function definition (where the body of the function is provided) might take a fixed number of arguments, and might even have a prototype.
When such a function declaration is used, it is up to the programmer making a call to foo() to get the arguments that foo() expects (and their types) correct - the compiler has no information about the argument list foo() requires. It also restricts the type of parameters that the function definition can use because when the function is called without a prototype, therefore the default argument promotions will be applied to the arguments at the function call. So a function that's declared without a prototype can only expected to get promoted arguments.
See the following C99 standard sections for some details:
6.7.5.3/14 "Function declarators (including prototypes)
...
The empty list in a function declarator that is not part of a
definition of that function specifies that no information about the
number or types of the parameters is supplied.
...
6.5.2.2 Function calls
...
If the expression that denotes the called function has a type that
does not include a prototype, the integer promotions are performed on
each argument, and arguments that have type float are promoted to
double. These are called the default argument promotions. If the
number of arguments does not equal the number of parameters, the
behavior is undefined. If the function is defined with a type that
includes a prototype, and either the prototype ends with an ellipsis
(, ...) or the types of the arguments after promotion are not
compatible with the types of the parameters, the behavior is
undefined. If the function is defined with a type that does not
include a prototype, and the types of the arguments after promotion
are not compatible with those of the parameters after promotion, the
behavior is undefined, except for the following cases:
one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is
representable in both types;
both types are pointers to qualified or unqualified versions of a character type or void.
The C function calling standard allows for a function to be called with zero or more arguments and the number of arguments may or may not match the function interface.
The way this works is that it is up to the caller to adjust the stack after the called function returns rather than the called function adjusting the stack unlike other standards such as Pascal which require the called function to manage the stack adjustment properly.
Because the caller knows what arguments and their types have been pushed onto the stack before the called function is called and the called function does not, it is up to the caller to clear the pushed arguments from the stack after the called function returns.
With updated C standards, the function call interface description has become more complex in order to allow the compiler to detect and report interface problems that the original K&R C standard allowed to go undetected by the compiler.
The standard now is that you specify variable argument lists using elipsis notation of three periods or dots after the last known and specified argument in the called functions interface specification or declaration.
So you would see something like the following for some of the Standard C Library I/O functions:
int sprintf (char *buffer, char *format, ...);
This indicates that the function sprintf requires that the first argument be a char pointer to a buffer, the second argument be a char pointer to a format string, and there may be other additional arguments. In this case any additional arguments would be what are needed to be inserted for the print format specifiers in the format string. If the format string is just a text string with no format specifies (something like %d for an integer for instance) then there would be no other arguments.
The newer C Standards specify a set of functions/macros for the use with variable argument lists, the varg functions. With these functions/macros the called function can step through the variable part of an argument list and process the arguments. These functions look something like the following:
int jFunc (int jj, char *form, ...)
{
va_list myArgs;
int argOne;
va_start (myArgs, form);
argOne = va_arg (myArgs, int);
va_end (myArgs);
return 0;
}
The problem that we have with variable argument lists is that C does not have a way of communicating the variable argument or even how many arguments. So the designer of the function has to provide a mechanism. In the case of the C Standard Library I/O functions this is done with the format that indicates the number of arguments following the format string by specifying format specifiers for each argument. And since there is nothing that does a consistency check, you can end up with a format string that specifies more or less than the actual arguments resulting in either garbage output or less output than expected.
Since modern C compilers have some degree of backwards compatibility for old C source code, that means that you can use some of the older constructs and the compiler will allow it though hopefully with a warning.
The new function interface specifications are designed to reduce the chances of using a function incorrectly. So the new standards recommend that you use the function interface declaration so that the compiler can help you by detecting interface problems and incorrect variable usage in the function call.
However if you want to be a risk taker you do not have to use this safety net so if you like you can just define a function with an empty argument list and wing it.
You might also find an answer I put into this question about currying in C that uses variable argument lists along with a way to determine how many arguments are provided.
As mentioned in many other answers, a function taking an unspecified number of arguments of unspecified type just means it's caller doesn't know about its definition. It's completely different from variadic functions where you must define with ... and then use va_list in stdarg.h to obtain the arguments
It's actually common in the past and there are many standard functions using that feature, for example open()/openat()/_open() with an optional last parameter
fd = open(filename, O_RDONLY);
fd = open(filename, O_CREAT | O_WRONLY, 0777);
See open() function parameters
main() is also a function that can receive varying number of arguments
int main();
int main(int argc, char **argv);
int main(int argc, char **argv, char **envp);
int main(int argc, char **argv, char **envp, char **apple);
In fact the Windows x64 calling convention is designed to support such unspecified number of arguments so it's a bit unfortunate for most modern usecases