Исправлена ​​ошибка, из-за которой разыменовываемый указатель типа наказывался нарушением псевдонимов. - PullRequest
24 голосов
/ 11 января 2012

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

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

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

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

и

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

входящая_буф и исходящая_буф определяются следующим образом:

char                    incoming_buf[LIBIRC_DCC_BUFFER_SIZE];

char                    outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];

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

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

Ответы [ 6 ]

39 голосов
/ 11 января 2012

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

Правила алиасинга просто говорят, что вы можете получить доступ к объекту только через его собственный тип, его вариантный вариант со знаком или без знака.или через символьный тип (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);

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

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

21 голосов
/ 11 января 2012

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

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

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

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

4 голосов
/ 06 сентября 2016
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. единственная практическая причина использования приведенного выше кода - избегать предупреждений компилятора, КОГДА и ЕСЛИ вы знаете, что делаете

0 голосов
/ 27 февраля 2019

Если у вас есть причины, по которым вы не можете изменить тип исходного объекта (как это было в моем случае), и вы абсолютно уверены, что код правильный и он делает то, что намеревался сделать с этим массивом символов, Во избежание предупреждений вы можете делать следующее:

unsigned int* buf = (unsigned int*)dcc->incoming_buf;
unsigned int received_size = ntohl (*buf);
0 голосов
/ 16 марта 2018

Если можно, IMHO, для этого случая проблема заключается в разработке API-интерфейсов ntohl и htonl и связанных функций. Они не должны были быть записаны как числовой аргумент с числовым возвращением. (и да, я понимаю точку оптимизации макросов) Они должны были быть разработаны как сторона 'n', являющаяся указателем на буфер. Когда это будет сделано, вся проблема исчезнет, ​​и процедура будет точной, в зависимости от того, где находится хост. Например (без попытки оптимизации):

inline void safe_htonl(unsigned char *netside, unsigned long value) {
    netside[3] = value & 0xFF;
    netside[2] = (value >> 8) & 0xFF;
    netside[1] = (value >> 16) & 0xFF;
    netside[0] = (value >> 24) & 0xFF;
};
0 голосов
/ 10 июля 2015

Привести указатель к unsigned и затем вернуться к указателю.

unsigned int receive_size = ntohl (* ((unsigned *) ((unsigned) dcc-> входящая_buf)));

...