There are a couple of issues with the code as written.
First of all, you are invoking undefined behavior by trying to print the numeric representation of a char
object using the %d
conversion specifier:
Online C 2011 draft, §7.21.6.1, subclause 9:
If a conversion specification is invalid, the behavior is undefined.282) If any argument is
not the correct type for the corresponding conversion specification, the behavior is
undefined.
Yes, objects of type char
are promoted to int
when passed to variadic functions; printf
is special, and if you want the output to be well-defined, then the type of the argument and the conversion specifier must match up. To print the numeric value of a char
with %d
or unsigned char
argument with %u
, %o
, or %x
, you must use the hh
length modifier as part of the conversion spec:
printf( "%hhd ", *p );
The second issue is that the line
char *p = &y;
is a constraint violation - char *
and int *
are not compatible types, and may have different sizes and/or representations2. Thus, you must explicitly cast the source to the target type:
char *p = (char *) &y;
The one exception to this rule occurs when one of the operands is void *
; then the cast isn't necessary.
Having said all that, I took your code and added a utility that dumps the address and contents of objects in the program. Here's what y
, p
, and j
look like on my system (SLES-10, gcc 4.1.2):
Item Address 00 01 02 03
---- ------- -- -- -- --
y 0x7fff1a7e99cc d2 04 00 00 ....
p 0x7fff1a7e99c0 cc 99 7e 1a ..~.
0x7fff1a7e99c4 ff 7f 00 00 ....
j 0x7fff1a7e99b8 cc 99 7e 1a ..~.
0x7fff1a7e99bc ff 7f 00 00 ....
I'm on an x86 system, which is little-endian, so it stores multi-byte objects starting with the least-significant byte at the lowest address:
BE: A A+1 A+2 A+3
+----+----+----+----+
y: | 00 | 00 | 04 | d2 |
+----+----+----+----+
LE: A+3 A+2 A+1 A
On a little-endian system, the addressed byte is the least-significant byte, which in this case is 0xd2
(210
unsigned, -46
signed).
In a nutshell, you're printing the signed, decimal representation of that single byte.
As for the broader question, the type of the expression *p
is char
and the type of the expression *j
is int
; the compiler simply goes by the type of the expression. The compiler keeps track of all objects, expressions, and types as it translates your source to machine code. So when it sees the expression *j
, it knows that it's dealing with an integer value and generates machine code appropriately. When it sees the expression *p
, it knows it's dealing with a char
value.
- Admittedly, almost all modern desktop systems that I know of use the same representations for all pointer types, but for more oddball embedded or special-purpose platforms, that may not be true.
- § 6.2.5, subclause 28.
sizeof(int)
does not necessarily== 4
. – Foreordain