2
int *int_pointer = malloc(10);

*int_pointer = 53200;

printf("The integer at byte #0 is set to: %d \n", (char) *int_pointer);

RESULT: -48

So I was just trying this out to see what would happen and I got this really unexpected result.

Why -48? How did it even turn into negative?

discussedtree
  • 233
  • 2
  • 4
  • 12
  • 2
    The parameters in the `...` part of functions accepting a variable number of parameters are converted according to [the usual arithmetic conversions](http://port70.net/~nsz/c/c99/n1256.html#6.3.1.8). You convert a value of type `int` to a value of type `char` first, then implicitly convert that resulting value back to `int`. What I can say is that your implementation treats `char` as signed :) – pmg Aug 01 '14 at 13:32

4 Answers4

8

Language-lawyer perspective:

I believe that correct reference in C99/C11 standard is §6.3.1.3 (with emphasis mine):

  1. When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
  2. Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
  3. Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

Note that char type is problematic, since it's also implementation-defined if it's actually represented as signed or unsigned. The only way that is fully-defined by standard itself is cast to unsigned char type.

Practical view:

Assuming that for your implementation sizeof(int) == 4 and it uses two's complement method for storing signed integers, the number 53200 is represented as:

0000 0000 0000 0000 1100 1111 1101 0000

Note that if you have little-endian CPU (probably it's true assumption), then order of bytes, or more strictly how they are actually stored in memory and CPU registers is inverted,i.e. that number is stored as:

0000 1101 1111 1100 0000 0000 0000 0000

What (unsigned char) 53200 produces is result of (in purely mathematical sense) substraction as (note that standard guarantees that sizeof(unsigned char) == 1):

53200 - 256 - 256 - ... - 256 = 53200 % 256 = 208

which is in binary 1101 0000

It can be mathematically proven that result is always the same as "cut-off", resting only last, least-significant byte as result of cast.

Note for printf():

As pointed by @pmg printf() is variadic function and due to default argument promotion its optional arguments of type unsigned char (or signed char and char as well) are always promoted to int, but this time it's "just a formality".

Note for alternative biwise operator solution:

As an alternative solution you can use biwise & and operator with proper mask to obtain least-significant byte of particular number, for example:

*int_number & 0xff   /* mask is 0000 0000 0000 0000 1111 1111 */
Grzegorz Szpetkowski
  • 36,988
  • 6
  • 90
  • 137
5

Casting a data type into another data type that isn't wide enough to hold all possible values is undefined behaviour. With undefined behaviour, the compiler is free to do whatever it pleases, so typically it does whatever is the least effort for the implementors, since they are automatically right and you are always wrong.

Therefore, you don't get to ask "Why did it happen?" - you should be glad you didn't get 52301, or 42, or "Help! I'm trapped in an integer library!" instead.

Kilian Foth
  • 13,904
  • 5
  • 39
  • 57
  • Adding to this answer. Your int_pointer can hold a maximum of 10 bytes of data. You have stored 4 bytes of data in int_pointer. A char data type can only hold 1 byte of data. So, there is an overflow when you cast 4 bytes to 1 byte. That's why your number is being returned as -48. – Cameron McKay Aug 01 '14 at 13:08
  • To add: what the compiler did here was just to take the lower 8 bits of the value and interpret those as a signed number. – Bart van Ingen Schenau Aug 01 '14 at 13:13
  • I think that there is no UB here, as casting between integer types is well-defined in C standard (for signed types it's implementation-defined) – Grzegorz Szpetkowski Aug 01 '14 at 13:53
2

That's simple: 53200 = 0xCFD0 and 0xCF = 207 which for a signed char = -48 ...

Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
  • @GrzegorzSzpetkowski maybe it's not standard-defined, but it's what happens. For this run of this version compiled with this compiler with these options on this OS, but still. – Quentin Aug 01 '14 at 12:13
  • 1
    @Quentin: I already found that it's actually implementation defined, see C99/C11 6.3.1.3. – Grzegorz Szpetkowski Aug 01 '14 at 12:14
  • 1
    I'm confused. Doesn't a cast from int to char in C always truncate the value, retaining only the low-order 8 bits? – Hot Licks Aug 01 '14 at 12:17
  • 1
    @HotLicks not if the destination type is signed and the value doesn't fit. – Quentin Aug 01 '14 at 12:23
  • 2
    @HotLicks No, it is implementation-defined. You may be used to that behaviour on systems you have experience with, but there are a wide range of platforms that C has been used on. – M.M Aug 01 '14 at 12:34
1

I think @fritzone is correct..
Since the range of the integers in the C is -32768 to 32767 and hence after 32767 it will go for -32768 rather than 32768 and hence it is printing -48 in the place of 53200.
Try the value 53201 and it will print out the value -47 and so on..

viveksinghggits
  • 661
  • 14
  • 35