Исправление для указателя с типами дерферинга сломает строгое устранение
-
27-10-2019 - |
Вопрос
Я пытаюсь исправить два предупреждения при составлении конкретной программы с использованием GCC. Предупреждения:
Предупреждение: указатель с типом Dereferencing пробит правила строгого аодезирования [-wstrict-aliassing
И эти два виновника:
unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));
а также
*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);
overment_buf а также исходящий_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
. Анкет Действительно результат оператора Dereference в выражении *((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);
РЕДАКТИРОВАТЬ:
Я полностью переработал свой ответ, в частности, я объясняю, почему программа получает предупреждения от компилятора.
Другие советы
Решить проблему, Не каламбур и псевдоним! Единственный «правильный» способ прочитать тип T
для распределения типа T
и заполняйте его представление, если это необходимо:
uint32_t n;
memcpy(&n, dcc->incoming_buf, 4);
Короче говоря: если вы хотите целое число, вам нужно сделать целое число. Там нет способа обмануть это в языке, поддерживаемом языком.
Единственное преобразование указателя, которое вам разрешено (для целей ввода -вывода), - это обработка адреса существующая переменная типа T
как char*
, или, скорее, как указатель на первый элемент массива размеров sizeof(T)
.
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. Единственная практическая причина использования приведенного выше кода - избежать предупреждений компилятора, когда и если вы знаете, что делаете
Если я могу, ИМХО, для этого случая проблема заключается в дизайне 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;
};
Если у вас есть причины, которые не позволяют вам изменить тип исходного объекта (как это было в моем случае), и вы абсолютно уверены, что код верен, и он делает то, что намеревалось сделать с этим массивом, чтобы избежать предупреждений, которые вы может сделать следующее:
unsigned int* buf = (unsigned int*)dcc->incoming_buf;
unsigned int received_size = ntohl (*buf);
Укажите указатель без знака, а затем вернуться к указателю.
unsigned int ected_size = ntohl ( *((unsigned *) ((без знака) dcc-> outment_buf)));