2

Why does the following code behave differently on iPhone simulator and device? Im running the simulator on an intel macbook pro and the device is an iPhone 5 (model MD297KS/A).

Code:

uint8_t original = 23;
uint8_t * pointerToOriginal = &original;
uint32_t * casted = (uint32_t *)pointerToOriginal;
printf("original: %u\ncasted: %u\n", original, *casted);

Output when ran on simulator:

original: 23
casted: 23

Output when ran on device:

original: 23
casted: 2755278871

I assumed the cast would result in garbage data being included in the casted integer so the device output makes sense to me, but why is the integer unaffected by the extra data introduced in the cast on simulator?

sergej
  • 17,147
  • 6
  • 52
  • 89
quad16
  • 184
  • 5
  • 20
  • 1
    i assumed the cast would result in garbage data being included in the `casted` integer so the device output makes sense to me, but why is the integer unaffected by the extra data introduced in the cast on simulator? – quad16 Aug 29 '16 at 08:20
  • 2
    Probably the simulator zeroed (reset to `0`) all memory and endianness do the rest of the trick. I mean: on simulator the dereference result in `0x00000017`, but not on the real device where the memory contains garbage. – LPs Aug 29 '16 at 08:22
  • 1
    Isn't *undefined behavior* just grand. – WhozCraig Aug 29 '16 at 08:26
  • I tested copy-pasting the exact same code a few rows down in the same source file, ran it on simulator, and got `casted: 23` from the first printf and `casted: 1800798487` from the second printf. I was thinking maybe this meant something and i'd learn something but maybe i should just let it go? Undefined behaviour is what it is? – quad16 Aug 29 '16 at 08:42
  • 2
    [The behavior is not defined](https://en.wikipedia.org/wiki/Undefined_behavior), not predictable due to the memory that is dereferenced that can contain whatever value. Take a look at [this SO post](http://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior) – LPs Aug 29 '16 at 08:45
  • Thanks. I'm reading the post and learning a lot. By the way when i did the copy-paste test i used blocks, so after the first printf was done things were popped from the stack which then reappeared as garbage data in the second dereference and printf. Maybe. Im in slightly deep water now (learning as i go). – quad16 Aug 29 '16 at 08:58
  • Note that despite your question title, you are not performing a uint32_t cast, nor do you have a "casted integer" as described in your question. You are making a `uint32_t*` cast and are casting a pointer. There's a big difference! – jamesdlin Aug 29 '16 at 10:16
  • I understand there's a difference but i've always thought of pointer casts as casts with delayed conversion; the conversion happens at the time of dereference instead of immediately. Where can i read about how it really works? – quad16 Aug 29 '16 at 10:29
  • 1) Use proper printf specifiers when printing `uint32_t`. 2) Gain more insight by using hexadecimal rather than decimal specifiers. 3) "integer unaffected by the extra data introduced" has not been demonstrated. True there is no _value_ change. 4) Why can't "garbage data" be many bits of 0? – chux - Reinstate Monica Aug 29 '16 at 13:54
  • @Pärserk When you cast a pointer to a `T*`, you're telling the compiler to interpret it as pointer-to-`T` when that pointer is dereferenced. That is, assume that the pointer is properly aligned for type `T` and read `sizeof (T)` bytes starting at that address. `(T*) p` is not at all the same as `(T) *p` . There is no "conversion" of values (which could happen when, say, casting from a negative `int32_t` to `uint32_t` or when casting from a large `uint64_t` value to a narrower `uint32_t`); a pointer cast instead changes the way that memory is interpreted. – jamesdlin Aug 30 '16 at 23:30

2 Answers2

5

From the C11 standard "6.3.2.3-7":

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. ...

From the C11 standard "J.2":

The behavior is undefined in the following circumstances: ...

  • Conversion between two pointer types produces a result that is incorrectly aligned (6.3.2.3).

Emphasis mine

sergej
  • 17,147
  • 6
  • 52
  • 89
2

First of all your code will lead to undefined behavior. But to make things clear i will try to explain what is going on.

original is stored on stack. So when you take pointer to original you will get pointer to region with length 8bit in stack memory (this information available only for compiler). Like so:

  byte 0     byte 1    byte 2    byte 3
  [00010111][????????][????????][????????]

Lets say that stack starts from address 0. So pointerToOriginal will point to byte at address 0. Compiler know that pointerToOriginal points to 8bit value(because of it's type). So when unreferencing it will read exactly 1 byte starting from address 0. But when converting uint8_t* to uint32_t* you actually force compiler to read 4 bytes instead of 1. So you will end up with reading 4 bytes 3 of which going to be junk. On the simulator looks like memory region is filled with zero. So stack will looks like so:

  byte 0     byte 1    byte 2    byte 3
  [00010111][00000000][00000000][00000000]

and when You dereferencing casted you will get 23 back. But on real machine it will contain only junk.

Illustration above not explaining one more advanced stuff - Big and Little Endian.

j2ko
  • 2,479
  • 1
  • 16
  • 29