Q. How can I write functions that take a variable number of arguments?
Use <stdarg.h>. This defines some macros that let your program deal with variable numbers of arguments.
There's no portable way for a C function, with no constraints on what it might be passed, to know how many arguments it might have gotten or what their types are. If a C function doesn't take a fixed number of arguments (of fixed types), it needs some convention for what the arguments are. For example, the first argument to printf is a string, which indicates what the remaining arguments are:
printf("Hello, world!\n"); /* no more arguments */
printf("%s\n", "Hello, world!"); /* one more string argument */
printf("%s, %s\n", "Hello", "world!"); /* two more string arguments */
printf("%s, %d\n", "Hello", 42); /* one string, one int */
The below program shows a simple printf-like function. The first argument is the format; from the format string, the number and types of the remaining arguments can be determined. As with the real printf, if the format doesn't match the rest of the arguments, the result is undefined. There's no telling what your program will do then (but probably something bad).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
static char * int2str(int n)
{
int minus = (n < 0);
static char buf[32];
char *p = &buf[31];
if (minus)
n = -n;
*p = '\0';
do {
*--p = '0' + n % 10;
n /= 10;
} while (n > 0);
if (minus)
*--p = '-';
return p;
}
/* This is a simple printf-like function that handles only
the format specifiers %%, %s, and %d. */
void simplePrintf(const char *format, ...)
{
va_list ap; /* ap is our argument pointer. */
int i;
char *s;
/* Initialize ap to start with the argument after "format" */
va_start(ap, format);
for ( ; *format; format++)
{
if (*format != '%')
{
putchar(*format);
continue;
}
switch (*++format)
{
case 's':
/* Get next argument (a char*) */
s = va_arg(ap, char *);
fputs(s, stdout);
break;
case 'd':
/* Get next argument (an int) */
i = va_arg(ap, int);
s = int2str(i);
fputs(s, stdout);
break;
case '\0':
format--;
break;
default:
putchar(*format);
break;
}
}
/* Clean up varying arguments before returning */
va_end(ap);
}
void main()
{
simplePrintf("The %s tax rate is %d%%.\n", "sales", 6);
}
Q. Why should I use standard library functions instead of writing my own?
The standard library functions have three advantages: they work, they're efficient, and they're portable. They work: Your compiler vendor probably got them right. More important, the vendor is likely to have done a thorough test to prove they're right, more thorough than you probably have time for. (There are expensive test suites to make that job easier.)
They're efficient: Good C programmers use the standard library functions a lot, and good compiler vendors know that. There's a competitive advantage for the vendor to provide a good implementation. When competing compilers are compared for efficiency, a good compiler implementation can make all the difference. The vendor has more motivation than you do, and probably more time, to produce a fast implementation.
They're portable: In a world where software requirements change hourly, the standard library functions do the same thing, and mean the same thing, for every compiler, on every computer. They're one of the few things you, as a C programmer, can count on.
The funny thing is, one of the most standard pieces of information about the standard library is hard to find. For every function, there's one header file (or, rarely, two) that guarantees to give you that function's prototype. (You should always include the prototype for every function you call;) What's funny? That header file might not be the file that actually contains the prototype. In some (sad!) cases, it's not even the header file recommended by the compiler manual. The same is true for macros, typedefs, and global variables.
No comments:
Post a Comment