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

StackOverflow https://stackoverflow.com/questions/8824622

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top