I encountered a strange problem when casting and modifying pointers on a 32bit embedded system (redbee econotag running contiki OS to be specific).
uint32_t array[2];
array[0] = 0x76543210;
array[1] = 0xfedcba98;
uint8_t* point = ((uint8_t*)array)+1;
printf("%08x \n", *(uint32_t*)point );
output on my computer:
98765432
output on embedded device:
10765432
My computer behaves as I expect it to, the embedded device however seems to wrap around when it reaches the end of the word. Why does this happen?
Your target "redbee econotag" is stated as an ARM7 which has ARMv4 architecture. ARMv4 doesn't provide unaligned memory access like an ARMv7 or an intel machine.
Quoting from ARM's documentation:
On ARMv4 and ARMv5 architectures, and on the ARMv6 architecture depending on how it is configured, care needs to be taken when accessing unaligned data in memory, lest unexpected results are returned. For example, when a conventional pointer is used to read a word in C or C++ source code, the ARM compiler generates assembly language code that reads the word using an LDR instruction. This works as expected when the address is a multiple of four, for example if it lies on a word boundary. However, if the address is not a multiple of four, the LDR returns a rotated result rather than performing a true unaligned word load. Generally, this rotation is not what the programmer expects.
With this code, you break the strict aliasing rule: the object pointed by point
is accessed by an lvalue expression that has uint32_t
type.
C11 (n1570), § 6.5 Expressions
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
This leads to an undefined behavior, so anything can happen.
C11 (n1570), § 4. Conformance
If a ‘‘shall’’ or ‘‘shall not’’ requirement that appears outside of a constraint or runtimeconstraint is violated, the behavior is undefined.
Because of the +1
you do an unaligned access of a 32-bit value, i.e. the address is not a multiple of four.
x86 works independently of alignment, because its roots go all the way back to 8-bit machines (maybe performance is slightly worse).
ARM requires alignment (as do many other processors), so 32-bit values should be placed at addresses that are a multiple of four bytes. Various bad things may happen if this isn't the case (wrong values, faults). For the array the compiler takes care of that, but when you explicitly cast the pointer you force it to violate alignment.
printf("%08x \n", *(uint32_t*)point );
The *
expression in this statement invokes undefined behavior: it violates aliasing rules and may do unaligned access.
EDIT: please note that the body of this answer is made irrelevant by the comments it prompted
The theory of the other answers is fine, but probably doesn't help you. The actual problem is that you wrote:
uint8_t* point = ((uint8_t*)array)+1;
When you should have written something such as
uint8_t* point = (uint8_t*)(array+1);
because you need to increment the pointer as a pointer to an appropriate type (so that the increment operation will add the size of an element), before you cast it to something else.
But one might ask if you really intend to have a byte pointer to a 32-bit value. Perhaps you do intend to access it in bytewise fashion (beware that the byte ordering will vary between systems!). Or perhaps you really intended for point to be a pointer to a 32-bit value which is in turn a pointer to an 8-bit value somewhere else...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With