Section 15. Variable-Length Argument Lists 15.1: I heard that you have to #include before calling printf(). Why? A: So that a proper prototype for printf() will be in scope. A compiler may use a different calling sequence for functions which accept variable-length argument lists. (It might do so if calls using variable-length argument lists were less efficient than those using fixed-length.) Therefore, a prototype (indicating, using the ellipsis notation "...", that the argument list is of variable length) must be in scope whenever a varargs function is called, so that the compiler knows to use the varargs calling mechanism. References: ISO Sec. 6.3.2.2, Sec. 7.1.7; Rationale Sec. 3.3.2.2, Sec. 4.1.6; H&S Sec. 9.2.4 pp. 268-9, Sec. 9.6 pp. 275-6. 15.2: How can %f be used for both float and double arguments in printf()? Aren't they different types? A: In the variable-length part of a variable-length argument list, the "default argument promotions" apply: types char and short int are promoted to int, and float is promoted to double. (These are the same promotions that apply to function calls without a prototype in scope, also known as "old style" function calls; see question 11.3.) Therefore, printf's %f format always sees a double. (Similarly, %c always sees an int, as does %hd.) See also questions 12.9 and 12.13. References: ISO Sec. 6.3.2.2; H&S Sec. 6.3.5 p. 177, Sec. 9.4 pp. 272-3. 15.3: I had a frustrating problem which turned out to be caused by the line printf("%d", n); where n was actually a long int. I thought that ANSI function prototypes were supposed to guard against argument type mismatches like this. A: When a function accepts a variable number of arguments, its prototype does not (and cannot) provide any information about the number and types of those variable arguments. Therefore, the usual protections do *not* apply in the variable-length part of variable-length argument lists: the compiler cannot perform implicit conversions or (in general) warn about mismatches. See also questions 5.2, 11.3, 12.9, and 15.2. 15.4: How can I write a function that takes a variable number of arguments? A: Use the facilities of the header. Here is a function which concatenates an arbitrary number of strings into malloc'ed memory: #include /* for malloc, NULL, size_t */ #include /* for va_ stuff */ #include /* for strcat et al. */ char *vstrcat(char *first, ...) { size_t len; char *retbuf; va_list argp; char *p; if(first == NULL) return NULL; len = strlen(first); va_start(argp, first); while((p = va_arg(argp, char *)) != NULL) len += strlen(p); va_end(argp); retbuf = malloc(len + 1); /* +1 for trailing \0 */ if(retbuf == NULL) return NULL; /* error */ (void)strcpy(retbuf, first); va_start(argp, first); /* restart; 2nd scan */ while((p = va_arg(argp, char *)) != NULL) (void)strcat(retbuf, p); va_end(argp); return retbuf; } Usage is something like char *str = vstrcat("Hello, ", "world!", (char *)NULL); Note the cast on the last argument; see questions 5.2 and 15.3. (Also note that the caller must free the returned, malloc'ed storage.) See also question 15.7. References: K&R2 Sec. 7.3 p. 155, Sec. B7 p. 254; ISO Sec. 7.8; Rationale Sec. 4.8; H&S Sec. 11.4 pp. 296-9; CT&P Sec. A.3 pp. 139-141; PCS Sec. 11 pp. 184-5, Sec. 13 p. 242. 15.5: How can I write a function that takes a format string and a variable number of arguments, like printf(), and passes them to printf() to do most of the work? A: Use vprintf(), vfprintf(), or vsprintf(). Here is an error() function which prints an error message, preceded by the string "error: " and terminated with a newline: #include #include void error(char *fmt, ...) { va_list argp; fprintf(stderr, "error: "); va_start(argp, fmt); vfprintf(stderr, fmt, argp); va_end(argp); fprintf(stderr, "\n"); } See also question 15.7. References: K&R2 Sec. 8.3 p. 174, Sec. B1.2 p. 245; ISO Secs. 7.9.6.7,7.9.6.8,7.9.6.9; H&S Sec. 15.12 pp. 379-80; PCS Sec. 11 pp. 186-7. 15.6: How can I write a function analogous to scanf(), that calls scanf() to do most of the work? A: C9X will support vscanf(), vfscanf(), and vsscanf(). (Until then, you may be on your own.) References: C9X Secs. 7.3.6.12-14. 15.7: I have a pre-ANSI compiler, without . What can I do? A: There's an older header, , which offers about the same functionality. References: H&S Sec. 11.4 pp. 296-9; CT&P Sec. A.2 pp. 134-139; PCS Sec. 11 pp. 184-5, Sec. 13 p. 250. 15.8: How can I discover how many arguments a function was actually called with? A: This information is not available to a portable program. Some old systems provided a nonstandard nargs() function, but its use was always questionable, since it typically returned the number of words passed, not the number of arguments. (Structures, long ints, and floating point values are usually passed as several words.) Any function which takes a variable number of arguments must be able to determine *from the arguments themselves* how many of them there are. printf-like functions do this by looking for formatting specifiers (%d and the like) in the format string (which is why these functions fail badly if the format string does not match the argument list). Another common technique, applicable when the arguments are all of the same type, is to use a sentinel value (often 0, -1, or an appropriately-cast null pointer) at the end of the list (see the execl() and vstrcat() examples in questions 5.2 and 15.4). Finally, if their types are predictable, you can pass an explicit count of the number of variable arguments (although it's usually a nuisance for the caller to supply). References: PCS Sec. 11 pp. 167-8. 15.9: My compiler isn't letting me declare a function int f(...) { } i.e. with no fixed arguments. A: Standard C requires at least one fixed argument, in part so that you can hand it to va_start(). See also question 15.10. References: ISO Sec. 6.5.4, Sec. 6.5.4.3, Sec. 7.8.1.1; H&S Sec. 9.2 p. 263. 15.10: I have a varargs function which accepts a float parameter. Why isn't va_arg(argp, float) working? A: In the variable-length part of variable-length argument lists, the old "default argument promotions" apply: arguments of type float are always promoted (widened) to type double, and types char and short int are promoted to int. Therefore, it is never correct to invoke va_arg(argp, float); instead you should always use va_arg(argp, double). Similarly, use va_arg(argp, int) to retrieve arguments which were originally char, short, or int. (For analogous reasons, the last "fixed" argument, as handed to va_start(), should not be widenable, either.) See also questions 11.3 and 15.2. References: ISO Sec. 6.3.2.2; Rationale Sec. 4.8.1.2; H&S Sec. 11.4 p. 297. 15.11: I can't get va_arg() to pull in an argument of type pointer-to- function. A: The type-rewriting games which the va_arg() macro typically plays are stymied by overly-complicated types such as pointer-to- function. If you use a typedef for the function pointer type, however, all will be well. See also question 1.21. References: ISO Sec. 7.8.1.2; Rationale Sec. 4.8.1.2. 15.12: How can I write a function which takes a variable number of arguments and passes them to some other function (which takes a variable number of arguments)? A: In general, you cannot. Ideally, you should provide a version of that other function which accepts a va_list pointer (analogous to vfprintf(); see question 15.5 above). If the arguments must be passed directly as actual arguments, or if you do not have the option of rewriting the second function to accept a va_list (in other words, if the second, called function must accept a variable number of arguments, not a va_list), no portable solution is possible. (The problem could perhaps be solved by resorting to machine-specific assembly language; see also question 15.13 below.) 15.13: How can I call a function with an argument list built up at run time? A: There is no guaranteed or portable way to do this. If you're curious, ask this list's editor, who has a few wacky ideas you could try... Instead of an actual argument list, you might consider passing an array of generic (void *) pointers. The called function can then step through the array, much like main() might step through argv. (Obviously this works only if you have control over all the called functions.) (See also question 19.36.)