Jordan Abel wrote:
....
Things are only undefined because the standard says that they are
undefined, not for any other reason. ...
As it happens, the standard explicitly states that when there is no
explicit definition of the behavior, it is undefined. There is no part
of the standard anywhere that specifies the behavior of the printf()
family of functions when there is a mis-match between the format code
and the actual promoted type of the arguments.
... As it happens, this is in fact the
case, 7.19.6.1p9 "If any argument is not the correct type for the
corresponding conversion specification, the behavior is undefined".
However, if we assume that printf is a variadic function implemented
along the lines of the stdarg.h macros, we see 7.15.1.1p2 "...except for
the following cases: - one type is a signed integer type, the other type
is the corresponding unsigned integer type, and the value is
representable in both types" - In this case, the value is not, but "%u
is for unsigned ints" as a blanket statement would seem to be incorrect.
comp.std.c added - do the signed/unsigned exception, and the char*/void*
one, to va_arg type rules also apply to printf?
The standard doesn't require the use of the <stdarg.h> macros. Whatever
method is used must be interface compatible with those macros,
otherwise suitably cast function pointers couldn't be used to invoke
printf(). However, since the standard specifies that the behavior for
printf() is undefined in this case, that allows it do something other
than, or in addition to, using stdarg.h macros. For instance, the
implementation might provide as an extension some way of querying what
the actual type an argument was, even though the standard provides no
method of doing so. If printf() is implemented using that extension, it
can identify the type mis-match, and because the behavior is explicitly
undefined when there is such a mismatch, it's permitted to do whatever
the implementors want it to do; the most plausible choice would be a
run-time diagnostic sent to stderr; though assert() and abort() are
other reasonable options.
Incidentally, my copy of some c89 draft says that %u takes an int and
converts it to unsigned. c99 changes this to take an unsigned int. There
are several possibilities: Perhaps they screwed up (but the c99
rationale does not comment on this), or they thought the difference was
insignificant enough not to matter.
I think that it was felt that the difference was important, and
desireable, but not worthy of an explicit mention in the Rationale. Do
you think the C99 specification is undesireable?