Parameter ...
This page was translated by a robot.
In the C and C++ languages, it is possible to ...leave open the exact number of expected arguments and their type in a function's parameter list by using three ellipses. Depending on the situation, more or less arbitrary arguments can be passed to such functions, which are designated as variadic , without the programmer having to program a new signature for every conceivable case. The most famous example of a variadic function is printf:
Declaration
Hello
Hello 42
Hello 42
Hello World
Hello W0r1d
This page provides a detailed description of the use of variadic functions and some implementation examples. For a simple listing of the required calls, refer to the description of the stdarglibrary .
Details
A variadic function can expect any number of arguments, but in C and C++ for variadic functions at least one fixed parameter must always be specified in the declaration. The additional parameters are ...specified with three ellipses and must always be declared as the last parameter. The three ellipses are called ellipsis in English . In German, this corresponds to the term ellipse .
Although the possibility of arbitrary arguments is tempting, variadic functions are rarely useful and have the disadvantage that the type of arguments passed cannot generally be checked by the compiler. This type uncertainty can lead to hard-to-detect problems, see below. Usually, however, a programmer knows very well how many and what kind of arguments should be passed to a function, which is why variadic functions are hardly used to this day and are even frowned upon. Nevertheless, there are one or two exceptions that have proven to be practical.
New mechanisms such as templates , function overloading or default values for parameters have been built into C++, which are far more practical and safer than variadic functions. Other languages have built in similar mechanisms or use arrays, for example, to pass any number of (both strict and loosely typed) arguments.
It should be noted that, in addition to variadic functions, there are also variadic macros , which are processed by the preprocessor.
Programming of Variadic Functions
In order to program variadic functions, the stdarglibrary must first be integrated.
Some macros are defined in the stdarglibrary, with which the additional arguments can be addressed. In the following example, a function determines the maximum of all passed numbers:
Define list
Initialize list
Get next argument
Close list
441
First a variable with the type va_listis defined. With va_startthen the list is initialized by giving the name of the last parameter which is before the ellipsis .... Then each call to va_argreturns the next argument with a type specification. At the end, the list must be va_endclosed with . These macros are described in more detail in the stdarglibrary .
It should be noted that the type va_listis a type like any other and thus argument lists themselves can be passed to other functions. Here is an example of a variadic function that vsnprintfpasses its argument list to a function.
It should be noted that the type va_listcan theoretically also be used as a return type. However, since the argument list is immediately invalidated upon its return, such an approach, with whatever intentions, is pointless and also dangerous.
Type Uncertainty
The additional arguments of variadic functions cannot generally be checked by the compiler for the correct use of a type. On the one hand, this means that within the variadic function, the programmer must assume responsibility for the correct use of types. Addressing a variable with the wrong type can understandably lead to an incorrect result. In the following example, a pointer to a is incorrectly intpassed, but within the function this is interpreted as inthaving no pointer.
It is also dangerous to use types that are automatically converted by the compiler using type promotion . In the following example, a is floatpassed, which is automatically converted into a by the compiler double:
Today's compilers, however, warn when such promotions occur. The solution to the floatconversion problem would be to read the additional parameter within the function using and then cast doubleit again as .float
A small note on this last example: the attentive reader will notice that printfis also a variadic function. The casting of the value floatis therefore superfluous, since printfa promotion to takes place immediately when is called double. Incidentally, this is the reason why values of type floator can be specified for the function doublewithout additional casting .printf
Special caution is required in this context with the null pointer . Some compilers define this as the integer value 0, which is thus intincluded in the parameter transfer. Understandably, however, this can cause problems on 64-bit systems.
Since no information about types is available to the compiler, the compiler can no longer guarantee const-safety for variadic functions. As a result, a variadic function can unintentionally change values of the calling function:
If the constvariable were also staticdeclared as , this would inevitably lead to a program crash in modern systems, the cause of which may only become clear after days of troubleshooting.
Interesting little tidbit: an older version of the example above used printInta call to instead of directly, printfand at some point the author realized that with newer compilers the result of this nasty example was actually constant, when it shouldn't be. Apparently, compilers constnow do optimizations on variables, even when the compiler is told not to do any optimizations. In concrete terms, the content of the variable awas saved in a register and printfsimply transferred again when it was used again. By calling another function printInt, however, the compiler has to hand over the reins to the basic principles of the language.
Determining the Number of Arguments
In addition to the type uncertainty, variadic functions also have the disadvantage that the total number of arguments passed within the function cannot be determined without further ado. There are different solutions for this. One solution is that the number of arguments passed is passed in one of the first arguments. A parameter (e.g. ) is often simply declared for a variadic function count, which must be specified explicitly when the function is called. Since variadic functions must always have at least one fixed parameter anyway, this solution is very common (see example above).
In the example of printf, the number of expected arguments is determined based on the content of the passed string (today's compilers even printfhave built-in checkers).
There would also be a solution to store the number of expected parameters using a global variable. However, this approach is both frowned upon and dangerous when it comes to doing parallel processing.
Another popular solution to counting the number of arguments passed is to use a sentinel . A sentinel designates that argument from whose position the remaining number of expected arguments is known. Very often the last argument is used as the sentinel. This Sentinel argument is filled with a default value when passed (very common ). If this value occurs within the variadic function when processing the additional arguments, it is clear from this point how many arguments are still available.NULL
Sentinels are not only used for variadic functions, see also the arguments of the mainfunction . However, the use of variadic functions is fairly widespread and so there are even some compilers that can recognize Sentinels, see Attributes .
Duplicate Argument Lists
As long as the additional arguments of a variadic function only have to be addressed once, the macros listed above are sufficient. However, if an argument list is to be processed more than once, there is a macro that copies va_copy(dst,src)the pointer to an argument list from one variable srcto a second variable . dstHere is a simple example of how an argument list is processed multiple times:
Such a duplication is mainly required if a part or even the entire argument list is to be forwarded to another function, or if an argument list is expected as an input parameter of the function to be programmed. At the latest when using the type va_listas a transfer parameter, a programmer may come across inexplicable phenomena in connection with variadic functions. It is then often sufficient to duplicate the argument list.
For a better understanding of how inexplicable phenomena occur or can be avoided, the author recommends considering how variadic functions actually work.
How do Variadic Functions Work?
Variadic functions are possible in C and C++ because these languages allow arguments to be pushed from right to left on the stack . For more information on how stacks are constructed, see the Call Stack page.
The most important thing a programmer needs to know to understand variadic functions is that the argument (of any number ) listed first (leftmost) in the function call operator is always located immediately at the stack pointer within the called function. Since at least one argument must be fixed, you can use the last fixed parameter before the ellipsis at runtime...the position of the additional arguments relative to the stack pointer can be determined. By additionally specifying the expected type of each individual following argument, all arguments can be determined one after the other by working backwards through the stack step by step. This is highly efficient both in terms of runtime and memory space.
The additional arguments of a variadic function can therefore only be reached by specifying the last fixed parameter. From a purely technical point of view, it would be possible in principle to provide a mechanism that could process variadic functions without fixed parameters, but this is not provided for in the scope of the language. There is no special keyword or other special syntactic solution for the first additional argument. Since the implementation can also vary slightly depending on the system, the additional arguments of variadic functions should always be stdargaddressed using the macros defined in the library.
Unfortunately, depending on the system, certain macros are not defined. In general, it is not advisable to simply define standard macros yourself if they do not exist. In fact, the author is not aware of any other solution. Since a programmer will sooner or later stumble over this obstacle, here is the definition of the macro va_copy, as it should probably work on most systems:
The other macros should definitely be available (by including the stdarglibrary ). However, to satisfy the curiosity of the reader and the author himself, here is a list of the varargmacros as they could possibly be implemented. The reader is welcome to think through and try out my solution, but it is not recommended to use it.