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:

  1. every instance of structure must located @ memory address divisible size of largest field in structure.

  2. each field in structure must located @ offset (within structure) divisible size of field itself.

exceptions:

  1. 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.

  2. 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

Popular posts from this blog

php - regexp cyrillic filename not matches -

c# - OpenXML hanging while writing elements -

sql - Select Query has unexpected multiple records (MS Access) -