Variadic functions accept a variable number of arguments but are problematic. Variadic functions define an implicit contract between the function writer and the function user that allows the function to determine the number of arguments passed in any particular invocation. Failure to exercise care when invoking a variadic function to ensure that it knows when to stop processing arguments enforce this contract may result in undefined behavior.
...
Wiki Markup |
---|
In the following code example, the variadic function {{average()}} is used to determinecalculates the average value of its passedthe positive integer arguments passed to the function \[[Seacord 05c|AA. C References#Seacord 05c]\]. The function processes arguments until it encounters findsan oneargument with athe value of {{va_eol}} ({{\-1}}). |
Code Block |
---|
enum { va_eol = -1 }; unsigned int average(int first, ...) { unsigned int count = 0; unsigned int sum = 0; int i = first; va_list args; va_start(args, first); while (i != va_eol) { sum += i; count++; i = va_arg(args, int); } va_end(args); return(count ? (sum / count) : 0); } |
Note that va_start()
must be called to initialize the argument list and that va_end()
must always be called when finished with a variable argument list.
Non-Compliant Code Example
In this non-compliant code example, the average()
function above is called as follows:
Code Block | ||
---|---|---|
| ||
int avg = average(1, 4, 6, 4, 1); |
The omission of the va_eol
terminating value means that the function will continue to process values from the stack until it encounters a va_eol
by coincidence or an error occurs.
Compliant Solution
The following call maintains This compliant solution enforces the contract by adding a va_eol
as the last final argument.
Code Block | ||
---|---|---|
| ||
int avg = average(1, 4, 6, 4, 1, va_eol); |
...
Another common mistake is to use more format conversion specifiers than supplied arguments. This results in undefined behavior, for example, extracting non-existent arguments off the stack and unintentionally exposing data. The following example illustrates this:, as shown in this non-compliant coding example.
Code Block | ||
---|---|---|
| ||
char const *error_msg = "Resource not available to user."; /* ... */ printf("Error (%s): %s", error_msg); |
This results in non-existent arguments being processed by the function, potentially leaking information about the process.
Compliant Solution
The following code This compliant solution matches the number of format specifiers with the number of variable arguments.
...