Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unspecified number of parameters in C functions - void foo()

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.

like image 975
Jacob Krieg Avatar asked Feb 27 '14 16:02

Jacob Krieg


People also ask

What is void foo in C?

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.

What does the declaration void * foo () mean?

In the function declaration void foo(int) , the void signifies that “ foo does not return a value”. As a parameter type list. In the function declaration char bar(void) , the void signifies that “ bar has zero parameters”. As the target “type” of a pointer.

Can a void function have parameters C?

In C++, there is no difference in main() and main(void) . But in C, main() will be called with any number of parameters. main(void) will be called without any parameters. If we try to pass it then this ends up leading to a compiler error.

How many parameters can a function have in C?

The maximum number of arguments (and corresponding parameters) is 253 for a single function.


4 Answers

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).

like image 128
Keith Thompson Avatar answered Sep 22 '22 13:09

Keith Thompson


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.

like image 43
Vality Avatar answered Sep 18 '22 13:09

Vality


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.
like image 24
Michael Burr Avatar answered Sep 21 '22 13:09

Michael Burr


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.

like image 28
Richard Chambers Avatar answered Sep 19 '22 13:09

Richard Chambers