2013-10-07 2 views
-1

Say я имею следующую структуру:Порядка данных при литье массива на структуру

typedef struct MyStruct { 

    unsigned short a; /* 16 bit unsigned integer*/ 
    unsigned short b; /* 16 bit unsigned integer*/ 
    unsigned long c; /* 32 bit unsigned integer*/ 

}MY_STRUCT; 

и некоторые массивы данных (содержание только для демонстрации):

unsigned short data[] = {0x0011, 0x1100, 0x0001, 0x0FFF }; 

Тогда я выполнить folliwing:

MY_STRUCT *ms; 

ms = (MY_STRUCT *) data; 

printf("a is: %X\n",(*ms).a); 
printf("b is: %X\n",(*ms).b); 
printf("c is: %X\n",(*ms).c); 

Я хотел бы ожидать данные, которые должны быть считаны последовательно в мс, «слева направо», и в этом случае выходной сигнал будет :

a is: 11 
b is: 1100 
c is: 10FFF 

Однако то, что происходит на самом деле:

a is: 11 
b is: 1100 
c is: FFF0001 

Почему это происходит? Какое поведение следует ожидать при создании массивов таким образом?

+1

Маленькие конечные машины, такие как x86, хранят многобайтовые типы в обратном порядке. –

+4

Примечание: вы нарушаете [строгий псевдоним] (http://cellperformance.beyond3ad.com/articles/2006/06/understanding-strict-aliasing.html) с этим актом. Вам повезло, что компилятор не 32-битный-выровнять ведущих членов. – WhozCraig

ответ

1

Это потому, что машина, которой вы выполняете этот кусок кода, имеет мало порядковых байтов. Это означает, что он сохраняет свои байты в обратном порядке.

Число 0x4A3B2C1D будет храниться как 0x1D 0x2C 0x3B 0x4A.

Intel x86 - небольшая эндианская архитектура.

Причина, почему ваш a и b правильны, потому что вы храните short при создании данных, а затем вы загрузите shorts снова. Для c это немного отличается. Вы храните 2 shorts, но затем вы пытаетесь загрузить его как длинный. Вы не сохранили shorts, поскольку процессор сохранил бы их, если бы они были объединены как длинный, так что они будут отменены.

1

Какое поведение следует ожидать при создании массивов таким образом?

Ответ, это зависит. Добро пожаловать в удивительный мир Эндианс: http://en.wikipedia.org/wiki/Endianness

Суть в том, что вы считаете, что данные хранятся так, как вы ожидаете, что человек прочитает. Это большой конец. Вы, вероятно, на машине x86, однако, это немного аргумент. Это означает, что наиболее значимые цифры находятся в конце 4 байтов, а не в начале. Вот почему ваша вторая половина короткой появляется до первой половины вашего короткого замыкания.

Вы получите разные результаты по различным архитектурам с помощью этого метода.

+1

Выдающиеся, но x86 машины ** немного endian **; не большой. И сущность традиционно используется для описания того, что хранится * сначала * (последним из них является последствие). То есть Маленький-endian означает, что * наименее * важные байты сохраняются первыми; big-endian означает, что * большинство * значительных байтов сохраняются первыми. – WhozCraig

+0

Я только что понял и отредактировал. Мораль истории - не пытайтесь ответить SO в 2:40 утра. – keefer

+0

LOL. который все еще дает мне два часа. – WhozCraig

0

Как объяснили другие, результат зависит от цели. Кроме того, ваш код небезопасен и вызывает неопределенное поведение. Потому что нет никакой гарантии, что вы можете отбрасывать из структуры в короткий массив.

Это из-за выравнивания данных. Многие процессоры предпочитают или требуют, чтобы байты данных были распределены по четным адресам. Например, 32-разрядный ЦП с таким требованием согласования хотел бы, чтобы данные хранились по адресу, который делится на 4 (адреса соответствуют байтам, 4 байтам = 32 бита).

Если данные не хранятся на такой даже адрес, это смещена, что приведет к снижению производительности процессора на большинстве обычных 32/64-разрядных процессоров (86, PowerPC, ARM и т.д.) или, возможно, что код даже не может выполняться (редкий случай, я думаю, что применяется какой-то MIPS-процессор?).

Поэтому во время оптимизации компилятор пытается сохранить всех членов структуры по согласованным адресам. Это разрешено стандартом C: компилятор может свободно добавлять что-то под названием padding bytes, которое по существу является просто пространством для мусора, выделенным между членами структуры.

В вашем примере, компилятор для 32-разрядной большой Endian CPU может сделать что-то вроде этого:

Address   Data 
0x00000000  unsigned short a; MS byte 
0x00000001  unsigned short b; LS byte 
0x00000002  Padding byte 
0x00000003  Padding byte 
0x00000004  unsigned short b; MS byte 
0x00000005  unsigned short b; LS byte 
0x00000006  Padding byte 
0x00000007  Padding byte 
0x00000008  unsigned long c; MS byte 
0x00000009  unsigned long c; 
0x0000000A  unsigned long c; 
0x0000000B  unsigned long c; LS byte 

Как вы можете видеть, пытаясь интерпретировать этот кусок памяти как массив short дал бы вам проблемы, так как в итоге вы получите заполняющие байты в середине массива.

Формально, кастинг между структурами и массивами данных является неопределенным поведением и плохой практикой. Но существуют различные нестандартные расширения, которые позволят вам отключить отладку структуры, наиболее распространенным является #pragma pack. Если вы вызываете такой нестандартный параметр компилятора, то ваш код будет работать на практике.

+0

Однако, если вы работаете с 8-разрядными или 16-разрядными ЦП, у них, вероятно, нет требований к выравниванию и, следовательно, нет прошивки. Но стандарт C является общим и не зависит от типа CPU. – Lundin

Смежные вопросы