Dik said:
No, it is not well-defined. Passing a signed char passes an int.
%X expects an unsigned int, and at that stage no conversion takes
place. See 6.5.2.2#6, where there is talk about compatible types
in function calls, if the two are not compatible it is still
well-defined if one type is signed integer, the other unsigned
integer and the value representable for both. In the case of %X
this means that if a signed char is passed, it is only well-defined
if the value is >= 0.
Ok, that's what 6.5.2.2#6 says. At first this appears to
conflict with 6.3.1.1 which says that a signed char can be
used wherever an unsigned int can be used.
I was reading 6.3.1.1's "may be used" as "may be used,
with well-defined behaviour". But I think this is not correct:
although it says a signed char may be used, other constraints
may still apply that render it undefined.
For example:
char ch = -2;
isalpha(ch);
? isalpha's argument is a place where an int or unsigned int
may be used, but this causes undefined behaviour because
it violates the requirement that the argument to isalpha be
in the range 0 ... UCHAR_MAX.
If you agree so far, then it follows that 6.3.1.1 actually
doesn't have any bearing on the issue we were discussing.
My original position was that the rules in 7.19.6 (fprintf)
imply that passing the char to %X caused UB, and we
have just established that other rules can 'trump' 6.3.1.1.
If I can continue replying to your other post here instead of
having 2 sub-threads: it looks like the resolution is to say
that 7.19.6#7 is talking about the pre-promotion argument,
but #8 ("The int parameter to %d...") is talking about the
post-promotion argument. This is clearly the intent,
although it was not clear to me at first that the wording
expressed this intent accurately.
Have we resolved the case of:
char c = 0x20;
printf("%X\n", c);
?
6.5.2.2#6 says:
If the function is defined with a type that does not include
a prototype, and the types of the arguments after promotion
are not compatible with those of the parameters after
promotion, the behavior is undefined, except for the following
cases:
-- one promoted type is a signed integer type, the other
promoted type is the corresponding unsigned integer type,
and the value is representable in both types;
-- both types are pointers to qualified or unqualified versions
of a character type orvoid.
But the fprintf call is not a function defined with a type that does
not include a prototype. Even if we ignore that clause, and
assume that everything mentioned in 6.5.2.2#6 is part of
"default argument promotions", then how can you compare
the post-promotion argument type with the "type of the
parameter" ? The parameter is "..." which has no type.
If we assume that the definition of fprintf also provides
definitions of parameters to which 6.5.2.2#6 can be applied,
that would solve the problem.
A corollary of this would be that you can pass (char *)
and (unsigned char *) pointers as parameters to %p,
without having to cast them to (void *) first.