tags:

views:

116

answers:

3
#include <stdio.h>

int main(void)
{ 
   int x = 1000;
   char *ptr = &x;
   printf("%d\n",*ptr);
   return 0;
 }

Output: -24 /*In gcc 4.4.3 in Ubuntu 10.04 OS*/ With a warning: initialization from incompatible pointer type

What I think if the base type of the pointer were of int type then it would have retrieved 4 bytes from the location it's pointing.Then the O/P would have been 1000.But as I changed the base type to char then it would retrieve 1 byte from location it's pointing when I dereference it.But how the answer is -24. Again when I changed the program as below

#include <stdio.h>

    int main(void)
    { 
       int x = 1000;
       float *ptr = &x;
       printf("%f\n",*ptr);
       return 0;
     }

The output becomes 0.000000 with same warning.when I derefence the pointer it would retrieve 4 bytes from the location it's pointing.But how the O/P is 0.000000.I'm bit confused.Can u guys plz explain it.I new in C programming, so any mistake in asking the question, plz forgive me. Thanx

A: 

The core of your problem is that you attempted to print a char (*ptr) as an integer (%d).

Because you specified integer to printf by using %d, it will take 4 bytes off the stack to display.

However, because you passed a char to printf, you only passed 1 byte. The remaining 3 bytes that printf retrieves will be random stack data, and could be anything.

As a result, you got -24, or some other non-consistent value.

abelenky
@abelenky In the 1st snippet if I changed the %d to %c , then there is no output.Why ?
Parixit
because 1000 (or a 1-byte piece of 1000) is not a displayable character. I'll bet if you made x = 6565; there's a good chance you'd get the letter 'A'.
abelenky
@abelenky Hey when I take x= 6565, the O/P is : ?
Parixit
Somebody needs to start talking about hex here.
Hans Passant
-1 because your analysis is just wrong. In the first example `*ptr` interprets the first byte the pointer points to as a `char`. This `char` is then promoted to `int` and put on the stack of the `printf` call.
Jens Gustedt
@Jens: I do not see how the char is promoted to int when it is put on the stack. Remember that printf is a var_args function, and as such, the only fixed type is the first parameter (char*). All subsequent parameters do not have fixed types, so no automatic promotion can happen. If you pass a char, one byte is pushed on the stack. If you pass a double, 8 bytes are pushed. How those bytes are interpreted by printf is controlled entirely by the %d/%c/%f formatters found in the first string.
abelenky
@Hans Passant: You're right, I wrote 6565 in haste. What is more likely to produce "A" is x=0x4141; (that is two bytes of decimal value 65, expressed as hex). The dec. equivalent would be x=16705;
abelenky
@abelenky: va_arg parameters obey special rules. Relevant here is paragraph 7 of section 6.5.2.2 of the standard. It says `The default argument promotions are performed on trailing arguments.` So yes a `char` is promoted to `signed` or `unsigned int` depending on the implementation, `float` is promoted to `double` and so on.
Jens Gustedt
+3  A: 

Dereferencing a character pointer got you the first character-sized chunk of the internal representation of 1000 as an integer. That character was then promoted to a int (as per the rules for varidac arguments), and interpreted as integer.

On you machine, with that compiler, the result was -24.

Character sized chunk was probably a 8-bit byte.

The integer was probably represented by 4 or 8 of those bytes.

The internal representation was probably 2s-complement, and was probably stored in little endian order.

Do you begin to see why the result wasn't easy for you to predict?


Doing the same thing with a float pointer returned a float sized chuck of memory instead of a char, and now you may have real (heh!) trouble because we don't know, at this point if the float representation will even fit in a int representation, so you may be accessing uninitialized memory.

In any case, when you dereferenced the float* it interpreted that memory as float (which has a rather more complex internal structure than a character or even a 2s-complement integer), and promoted it to a double (those rules for varidac arguments again) then an attempt was made interpret that representation as an integer (and if the float did fit in an int, the double probably doesn't, so you're interpreting part of the double as an int!). Ugh.

This situation is even worse than the last because it involves the IEEE floating point representation standards and two chances to have the sizes not match up at all.

So the lesson here is:

Don't do that.

And the subsidiary lesson, only for experts is

Still don't do it.

because if you want to perform all these silly reinterpretations you want to have exact and explicit control of them.

dmckee
+1  A: 

1000 in hex is 0x3E8. If your system stores an int as a little-endian 32-bit value, then x is stored like this in memory:

+------+------+------+------+
| 0xE8 | 0x03 | 0x00 | 0x00 |
+------+------+------+------+
    ^
    |      ---increasing addresses--->
   &x

For the first case:

If your system has an 8-bit char, when you assign &x to char *ptr and then dereference it, you get the value 0xE8 interpreted as a char.

If your system treats the char type as signed, and if the signed representation is 2's complement, then 0xE8 is interpreted as -24.

For the second case:

If your system has a little-endian 32-bit float, when you assign &x to float *ptr and then dereference it, you get the value with a bit pattern of 0x000003E8 interpreted as float.

If your system represents float in the IEEE 754 format, then the most significant bit represents the sign (0 means it's positive), the next 8 most significant bits represents the exponent (0 means that the number is zero or denormalized - very small), and the remaining 23 bits represent the "significand" or "mantissa" (0x3E8 or 1000). This actually has the value 1000 * 2-149, which is approximately 1.4013 * 10-42 - you can see this value by using %g instead of %f in the printf format string.

Matthew Slattery