Плохое влияние значения после приведения типа

Я использую собственную беззнаковую длинную переменную в качестве буфера, который содержит внутри две беззнаковые короткие переменные. Насколько я знаю С++, это должен быть правильный метод. Я использовал этот метод для хранения 2 беззнаковых символов внутри одного беззнакового короткого много раз без каких-либо проблем. К сожалению, при использовании его на другой архитектуре он ведет себя странно. Кажется, он обновляет значение после второго присвоения. Случай (Overflow) существует просто для того, чтобы продемонстрировать это. Может ли кто-нибудь пролить свет на то, почему он так реагирует?

unsigned long dwTest = 0xFFEEDDCC;

printf("sizeof(unsigned short) = %d\n", sizeof(unsigned short));
printf("dwTest = %08X\n", dwTest);

//Address + values
printf("Addresses + Values: %08X <- %08X, %08X <- %08X\n", (DWORD)(&((unsigned short*)&dwTest)[0]), (((unsigned short*)&dwTest)[0]), (DWORD)(&((unsigned short*)&dwTest)[1]), (((unsigned short*)&dwTest)[1]) );

((unsigned short*)&dwTest)[0] = (WORD)0xAAAA;
printf("dwTest = %08X\n", dwTest);

((unsigned short*)&dwTest)[1] = (WORD)0xBBBB;
printf("dwTest = %08X\n", dwTest);

//(Overflow)
((unsigned short*)&dwTest)[2] = (WORD)0x9999;

printf("dwTest = %08X\n", dwTest);

Вывод Visual С++ 2010 (ОК):

sizeof(unsigned short) = 2
dwTest = FFEEDDCC
Addresses + Values: 0031F728 <- 0000DDCC, 0031F72A <- 0000FFEE

dwTest = FFEEAAAA

dwTest = BBBBAAAA

dwTest = BBBBAAAA

Выход ARM9 GCC Crosstool (не работает):

sizeof(unsigned short) = 2
dwTest = FFEEDDCC
Addresses + Values: 7FAFECD8 <- 0000DDCC, 7FAFECDA <- 0000FFEE

dwTest = FFEEDDCC

dwTest = FFEEAAAA

dwTest = BBBBAAAA

person Dunge    schedule 15.11.2011    source источник
comment
Скомпилируйте с включенными предупреждениями (-Wall) и посмотрите на предупреждения. Затем посмотрите здесь на SO для строгого псевдонима (решение: используйте союз).   -  person ninjalj    schedule 16.11.2011


Ответы (1)


То, что вы пытаетесь сделать, называется каламбуром. Есть два традиционных способа сделать это.

Способ сделать это с помощью указателей (что вы сделали). К сожалению, это конфликтует с оптимизатором. Видите ли, из-за проблемы остановки оптимизатор в общем случае не может знать, что два указателя не являются псевдонимами друг друга. Это означает, что компилятор должен перезагрузить любое значение, которое могло быть изменено с помощью указателя, что приводит к тоннам потенциально ненужных перезагрузок.

Итак, было введено правило строгого алиасинга. По сути, это говорит о том, что два указателя могут создавать псевдонимы друг друга только в том случае, если они одного типа. Как правило, char * может быть псевдонимом любого другого указателя (но не наоборот). Это устраняет каламбур с помощью указателей и позволяет компилятору генерировать более эффективный код. Когда gcc обнаруживает каламбур и включает предупреждения, он предупредит вас следующим образом:

warning: dereferencing type-punned pointer will break strict-aliasing rules

Другой способ сделать каламбур — через объединение:

union {
    int i;
    short s[2];
} u;
u.i = 0xDEADBEEF;
u.s[0] = 0xBABE;
....

Это открывает новую целую банку червей. В лучшем случае это зависит от реализации. Теперь у меня нет доступа к стандарту C89, но в C99 изначально указывалось, что значение члена объединения, отличного от последнего сохраненного, не указано. Это было изменено в TC, чтобы указать, что значения байтов, которые не соответствуют последнему элементу, сохраненному в, не указаны, и иначе указано, что байты, которые соответствуют последнему элементу, сохраненному в, интерпретируются заново в соответствии с новым type (то, что, очевидно, зависит от реализации).

Для С++ я не могу найти в стандарте язык о взломе союза. В любом случае, в С++ есть reinterpret_cast<>, и это то, что вы должны использовать для обозначения типов в С++ (используйте эталонный вариант reinterpret_cast<>).

В любом случае, вам, вероятно, не следует использовать типизацию (зависит от реализации), и вы должны создавать свои значения вручную с помощью битового сдвига.

person ninjalj    schedule 16.11.2011
comment
Спасибо за полный разъясненный ответ - person Dunge; 17.11.2011