Я пытаюсь исправить два предупреждения при компиляции конкретной программы с использованием 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];
Это кажется немного другим чем другие примеры этого предупреждения, которые я изучал. Я бы предпочел исправить проблему, а не отключать проверку строгого псевдонима.
Было много предложений использовать объединение - что может быть подходящим объединением для этого случая?
Прежде всего, давайте рассмотрим, почему вы получаете предупреждения о нарушении алиасинга.
Правила псевдонимов просто говорят, что вы можете получить доступ к объекту только через его собственный тип, его вариантный тип со знаком / без знака или через символьный тип (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);
РЕДАКТИРОВАТЬ:
Я полностью переработал свой ответ, в частности, объясняю, почему программа получает предупреждения о псевдонимах от компилятора.
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. единственная практическая причина использования кода выше - избегать предупреждений компилятора, КОГДА и ЕСЛИ вы знаете, что делаете
Чтобы решить проблему, не каламбур и псевдоним ! Единственный «правильный» способ чтения типа T
- это выделить тип T
и, при необходимости, заполнить его представление:
uint32_t n;
memcpy(&n, dcc->incoming_buf, 4);
Короче говоря: если вам нужно целое число, вам нужно сделать целое число Там нет никакого способа обмануть это языком.
Единственное преобразование указателя, которое вам разрешено (обычно для целей ввода / вывода), заключается в обработке адреса существующей переменной типа T
как char*
, или, скорее, в качестве указателя на первый элемент массива символов размера sizeof(T)
.