Исправить для разыменования тип-караульный указатель нарушит строгое сглаживание

Я пытаюсь исправить два предупреждения при компиляции конкретной программы с помощью GCC. Предупреждения:

предупреждение: разыменованный тип правила строгого сглаживания [-Wstrict-aliasing]

и два виновника:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));

и

*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);

incoming_buf и outgoing_buf определяются следующим образом:

char                    incoming_buf[LIBIRC_DCC_BUFFER_SIZE];

char                    outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];

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

Было много предложений по использованию союза - что может быть подходящим объединением для этого случая?

Ответы

Ответ 1

Прежде всего, давайте рассмотрим, почему вы получаете предупреждения о нарушении псевдонимов.

Правила псевдонимов просто говорят, что вы можете получить доступ только к объекту через его собственный тип, его тип с подписью/без знака или через тип символа (char, signed char, unsigned char).

C говорит, что нарушение правил псевдонимов вызывает поведение undefined (так не надо!).

В этой строке вашей программы:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));

хотя элементы массива incoming_buf имеют тип char, вы обращаетесь к ним как unsigned int. Действительно, результат оператора разыменования в выражении *((unsigned int*)dcc->incoming_buf) имеет тип unsigned int.

Это нарушение правил сглаживания, потому что вы имеете право только на доступ к элементам массива incoming_buf (см. сводку правил выше!) char, signed char или unsigned char.

Обратите внимание, что у вас точно такая же проблема с псевдонимом у вашего второго виновника:

*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);

Вы получаете доступ к элементам char outgoing_buf через unsigned int, поэтому это нарушение сглаживания.

Предлагаемое решение

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

unsigned int incoming_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
unsigned int outgoing_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];

(Кстати, ширина unsigned int определяется реализацией, поэтому вы должны использовать uint32_t, если ваша программа предполагает, что unsigned int - 32-разрядная).

Таким образом, вы можете хранить объекты unsigned int в своем массиве без нарушения правил псевдонимов, обратившись к элементу через тип char, например:

*((char *) outgoing_buf) =  expr_of_type_char;

или

char_lvalue = *((char *) incoming_buf);

EDIT:

Я полностью переработал свой ответ, в частности, объясню, почему программа получает предупреждения от псевдонимов от компилятора.

Ответ 2

Чтобы устранить проблему, не используйте каламбур и псевдоним! Единственным "правильным" способом чтения типа T является выделение типа T и заполнение его представления при необходимости:

uint32_t n;
memcpy(&n, dcc->incoming_buf, 4);

Вкратце: если вам нужно целое число, вам нужно сделать целое число. Там нет способа обманывать это по-испански.

Единственное преобразование указателя, которое вам разрешено (для целей ввода-вывода, в общем случае), относится к адресу существующей переменной типа T как char*, или, скорее, как указатель на первый элемент массива символов размером sizeof(T).

Ответ 3

union
{
    const unsigned int * int_val_p;
    const char* buf;
} xyz;

xyz.buf = dcc->incoming_buf;
unsigned int received_size = ntohl(*(xyz.int_val_p));

Упрощенное объяснение 1. Стандарт С++ утверждает, что вы должны попытаться самостоятельно выровнять данные, g++ - лишняя миля, чтобы генерировать предупреждения по этому вопросу. 2. Вы должны только попробовать, если вы полностью понимаете выравнивание данных в своей архитектуре/системе и внутри вашего кода (например, приведенный выше код является уверенным в Intel 32/64, выравнивание 1; Win/Linux/Bsd/Mac) 3. Единственная практическая причина использования вышеприведенного кода - избегать предупреждений компилятора, КОГДА и ЕСЛИ вы знаете, что вы делаете.

Ответ 4

Показ указателя на неподписанный, а затем обратно на указатель.

unsigned int received_size = ntohl (* ((unsigned *) ((без знака) dcc- > incoming_buf)));