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

Я пытаюсь исправить два предупреждения при компиляции конкретной программы с использованием 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];

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

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

24
задан hmijail 4 March 2013 в 16:59
поделиться

3 ответа

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

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

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

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

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);

РЕДАКТИРОВАТЬ:

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

40
ответ дан 28 November 2019 в 22:39
поделиться
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. Стандарт c ++ гласит, что вы должны попытаться выровнять данные самостоятельно, g ++ делает все возможное, чтобы генерировать предупреждения по этому вопросу. 2. вам следует пытаться делать это только в том случае, если вы полностью понимаете выравнивание данных в вашей архитектуре / системе и в своем коде (например, приведенный выше код - верная вещь для Intel 32/64; выравнивание 1; Win / Linux / Bsd / Mac) 3. единственная практическая причина использования кода выше - избегать предупреждений компилятора, КОГДА и ЕСЛИ вы знаете, что делаете

4
ответ дан 28 November 2019 в 22:39
поделиться

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

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

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

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

22
ответ дан 28 November 2019 в 22:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: