No, it does not tell the compiler (though there are some that will
check in the case of standard library functions). It tells the
function being called. (That is why you see messages in this group
that describe undefined behavior when someone passes an argument that
is incompatible with the conversion specification.) Look at the code
on page 156 again. If the conversion specification is %d, then the
code extracts an int. If it is %f, the code extracts a double. If it
is %s, the code extracts a char*. This all happens at run time, not
compile time.
The role of the char *fmt argument is to allow the same function to
handle different types and numbers of arguments.
..Please see below.
Why do you think
these functions are called variadic?
Wikipedia:"In computer programming, a variadic function is a function
of variable arity; that is, one which can take different numbers of
arguments."
I think they left out "and of different types".
Now, show us your code (the one with the first parameter declared as
an int) so we can explain what you are doing wrong, even if it is only
misinterpreting what you see in the debugger.
#include <stdio.h>
#include <stdarg.h>
int main (int argc, const char * argv[]) {
/*void minprintf(char *fmt, ...);*/
void minprintf(int fmt, ...);
minprintf("This is a test:\n%d:\n%s:\n%f:\n", 27, "More test",
-90.786);
/*warning: passing argument 1 of minprintf of incompatible pointer
type ( int fmt) */
return 0;
}
void minprintf(int fmt, ...)
/*void minprintf(char *fmt, ...)*/{
/* the role of char *fmt has been explained, but in 2 somewhat
different ways. Keith says "The *only* thing the "char *fmt, ..." part
of the declaration tells the compiler is that the first argument is of
type char*, and there
will be zero or more arguments of unspecified type(s) following that."
And Barry, you say that "The role of the char *fmt argument is to
allow the same function to handle different types and numbers of
arguments." Now my bet is that you are saying the same thing, but it
sounds somewhat different to me.
va_list ap;
/* So as Keith explained, ap is "an object type suitable for holding
information needed
by the macros va_start, va_arg, va_end, and va_copy" */
int ival;
double dval;
char *p, *sval;
va_start(ap, fmt);
/* So, here is the source of my confusion, and clearly the lack of
explaining it to the clc. I think this has been answered, so please
bear with me, but ap is now initialized to point to the first
unnamed argument. As Keith said "If the first argument were an int
rather than a char*, then minprintf wouldn't have any way of knowing
what argument values to grab." So I assume the initialization not only
points ap to the first argument, but it "types" the pointer as well,
and this information is conveyed by the expression "fmt". And,
rhetorically, I know the first argument is of type character, because,
""The *only* thing the "char *fmt, ..." part of the declaration tells
the compiler is that the first argument is of type char*" ( Keith).
For completeness,
debugger output (value of fmt) with declaration 'int fmt' =
1416128883 ( what I see in debugger as I step through the code)
debugger output (value of fmt) with declaration char *fmt = "This is
a test:\n%d:\n%s:\n%f:\n" ( as above)
debugger output (value of *ap) ( int fmt) = 0 '\0'
debugger output (value of *ap) ( char *fmt) = 0 '\0'
*/
for ( p=fmt; *p; p++){
/*So, the first argument, in my case "This is a test:\n%d:\n%s:\n%f:
\n" is now assigned to the char *p */
if ( *p != '%'){
putchar(*p);
continue;
}
switch (*++p) {
case 'd':
ival=va_arg(ap, int);
/* this part I get, I hope
. Each call of va_arg returns 1 argument
and steps ap to the next argument but in addition, types ap correctly
for the expected value, in the above case, and integer*/
printf("%d", ival);
break;
case 'f':
dval=va_arg(ap, double);
printf("%f", dval);
break;
case 's':
for ( sval = va_arg(ap, char*); *sval; sval++)
putchar(*sval);
break;
default:
putchar(*p);
break;
}
}
va_end(ap);
}