Generalnie pierwszy element ma znaczenie.
Natomiast chciałbym kolegom przypomnieć o istotnej pułapce, której zdaje się nie zauważać wielu z nas, a która "zadziała" na maszynach 16- i 32-bitowych. Załóżmy, że mam taki kod:
uint8_t buffer[16];
uint16_t z1;
uint32_t z2;
// tutaj kopiujemy coś do bufora, jakieś memcpy itp.
z1 = buffer[0]; // dobrze
z1 = buffer[1]; // źle
z1 = buffer[2]; // teoretycznie dobrze, zależnie od kompilatora i maszyny
z1 = buffer[3]; // źle
z2 = buffer[0]; // dobrze
z2 = buffer[1]; // źle
z2 = buffer[2]; // raczej źle, zależnie od kompilatora i maszyny
z2 = buffer[3]; // źle
Przy przypisywaniu danych z bufora do zmiennych należy uważać na wyrównanie danych w buforze. Jeżeli dane zaczynają się nieparzyście lub nie od wielokrotności 4 (lub 2 dla 16-bitowców), mogą pojawić się problemy. O wiele bezpieczniejszym sposobem jest wtedy wykorzystanie tradycyjnego memcpy:
memcpy(&z1, &buffer[1], sizeof(z1));
Jednak należy tutaj pamiętać o poprawnym endianesie - do takich operacji przydane jest mieć specjalne makra, które potrafią przepisać dany typ zmiennej bez względu na rodzaj maszyny, tj wykryć go w czasie kompilacji, albo mieć przygotowany zestaw dla big i little endian. Przykładowym moim makrem do kopiowania uint16_t z niewyrównanego bufora (który trzyma dane w big endian) jest:
#define _Int16ToVoidBE(buff, val) {_Lo(buff) = _Hi(val); _Hi(buff) = _Lo(val);}
gdzie makra _Lo i _Hi potrafią się odnieść do danych bajtów względem niewyrównanego adresu.
Tyle smaczków na dziś 🙂