gcc - Strange pointer arithmetic -
i came across strange behaviour of pointer arithmetic. developing program develop sd card lpc2148 using arm gnu toolchain (on linux). sd card sector contains data (in hex) (checked linux "xxd" command): fe 2a 01 34 21 45 aa 35 90 75 52 78 while printing individual byte, printing perfectly.
char *ch = buffer; /* char buffer[512]; */ for(i=0; i<12; i++) debug("%x ", *ch++);
here debug function sending output on uart. pointer arithmetic specially adding number not multiple of 4 giving strange results. uint32_t *p; // uint32_t typedef unsigned long.
p = (uint32_t*)((char*)buffer + 0); debug("%x ", *p); // prints 34012afe // correct p = (uint32_t*)((char*)buffer + 4); debug("%x ", *p); // prints 35aa4521 // correct p = (uint32_t*)((char*)buffer + 2); debug("%x ", *p); // prints 0134fe2a // strange??
am choosing wrong compiler option? pls help. tried optimization options -0 , -s; no change.
i think of little/big endian, here getting unexpected data (of previous bytes) , no order reversing.
your cpu architecture must support unaligned load , store operations.
to best of knowledge, doesn't (and i've been using stm32, arm-based cortex).
if try read uint32_t
value address not divisible size of uint32_t
(i.e. not divisible 4), in "good" case wrong output.
i'm not sure what's address of buffer
, @ least 1 of 3 uint32_t
read attempts describe in question, requires processor perform unaligned load operation.
on stm32, memory-access violation (resulting in hard-fault exception).
the data-sheet should provide description of processor's expected behavior.
update:
even if processor does support unaligned load , store operations, should try avoid using them, might affect overall running time (in comparison "normal" load , store operations).
so in either case, should make sure whenever perform memory access (read or write) operation of size n, target address divisible n. example:
uint08_t x = *(uint08_t*)y; // 'y' must point memory address divisible 1 uint16_t x = *(uint16_t*)y; // 'y' must point memory address divisible 2 uint32_t x = *(uint32_t*)y; // 'y' must point memory address divisible 4 uint64_t x = *(uint64_t*)y; // 'y' must point memory address divisible 8
in order ensure data structures, define them every field x
located @ offset divisible sizeof(x)
. example:
struct { uint16_t a; // offset 0, divisible sizeof(uint16_t), 2 uint08_t b; // offset 2, divisible sizeof(uint08_t), 1 uint08_t a; // offset 3, divisible sizeof(uint08_t), 1 uint32_t c; // offset 4, divisible sizeof(uint32_t), 4 uint64_t d; // offset 8, divisible sizeof(uint64_t), 8 }
please note, does not guarantee data-structure "safe", , still have make sure every mystruct_t*
variable using, pointing memory address divisible size of largest field (in example above, 8).
summary:
there 2 basic rules need follow:
every instance of structure must located @ memory address divisible size of largest field in structure.
each field in structure must located @ offset (within structure) divisible size of field itself.
exceptions:
rule #1 may violated if cpu architecture supports unaligned load , store operations. nevertheless, such operations less efficient (requiring compiler add nops "in between"). ideally, 1 should strive follow rule #1 even if compiler support unaligned operations, , let compiler know data aligned (using dedicated
#pragma
), in order allow compiler use aligned operations possible.rule #2 may violated if compiler automatically generates required padding. this, of course, changes size of each instance of structure. advisable use explicit padding (instead of relying on current compiler, may replaced @ later point in time).
Comments
Post a Comment