приведение затем разыменование указателей в C - PullRequest
4 голосов
/ 30 января 2012

При работе с буферами char в C иногда полезно и более эффективно работать с кусками данных размером int за раз.Для этого я могу привести свой char * к int * и использовать вместо него этот указатель.Однако я не совсем уверен, что это работает так, как я думаю.

Например, предположим, что у меня есть char *data, *(int32_t *)data = -1 всегда перезаписывает байты data[0], data[1], data[2] и data[3] а других байтов нет?

Ответы [ 3 ]

5 голосов
/ 30 января 2012

Расширение моего комментария.

Здесь есть две основные проблемы:


Нарушение строгого наложения имен технически неопределенное поведение . Вы можете псевдоним любого типа данных с char*, но не наоборот.

Вы можете обойти проблему с помощью f[no-]strict-aliasing в GCC.


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


Если производительность не является проблемой, в качестве полного способа проверки можно memcpy() создать буфер int.

Как только эти две проблемы будут решены, ваш пример с:

*(int32_t *)data = -1

перезапись data[0], data[1], data[2] и data[3] должна работать должным образом, если sizeof(int32_t) == 4. Просто обратите внимание на порядок байтов ...

3 голосов
/ 30 января 2012

Это технически неопределенное поведение, и в стандарте ничего не говорится о результатах наложения указателей на подобные. Специалист по стандартам сказал бы, что вызов такого неопределенного поведения может привести к чему угодно - от поврежденных данных до сбоя системы - Рагнаруку.

Прагматично, это зависит от вашего оборудования. Большинство современных систем (например, x86, x64, PPC, MIPS, ARM) обрабатывают записи размером с слово так, как вы описываете, за исключением того, что запись по невыровненному адресу приведет к сбою. Кроме того, это когда в игру вступает порядок байтов; в системе с прямым порядком байтов

char foo[4];
*((uint_32*)(foo)) = 0x01020304;
// the following are now true:
foo[0] == 0x04;
foo[1] == 0x03;
foo[2] == 0x02;
foo[3] == 0x01;

Короткий ответ: это небезопасно, если вы точно не знаете, на каком оборудовании будет работать ваша программа.

Если вы делаете , управляете оборудованием, для которого вы компилируете, тогда вы можете предсказать, что будет делать компилятор; Я использовал этот прием для ускорения упаковки байтовых массивов во встроенных системах.

1 голос
/ 30 января 2012

Нет, не обязательно. Если данные не выровнены правильно, они могут вообще не работать. Предполагая, что он выровнен правильно, он, вероятно, перезапишет следующие sizeof(int) байты и ничего больше, но я не уверен, что даже это полностью гарантировано.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...