Неверное влияние на значение после приведения типа - PullRequest
0 голосов
/ 16 ноября 2011

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

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 C ++ 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

1 Ответ

2 голосов
/ 17 ноября 2011

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

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

Итак, введено правило строгого наложения имен. В основном это говорит о том, что два указателя могут псевдоним друг друга, только когда они одного типа. Как особое правило, 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, чтобы указывать, что значения байтов, которые не соответствуют последнему сохраненному элементу, не указаны, и в противном случае указывалось, что байты, которые соответствуют последнему сохраненному элементу, интерпретируются повторно в соответствии с тип (что явно зависит от реализации).

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

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

...