Подтвердить что ты не робот

Разглаживающий тип-караульный указатель нарушит правила строгого сглаживания

У меня есть указатель без знака char, который содержит структуру. Теперь я хочу сделать следующее

unsigned char buffer[24];

//code to fill the buffer with the relevant information.

int len = ntohs((record_t*)buffer->len);

где структура record_t содержит поле с именем len.I не могу это сделать, и я получаю ошибку.

error: request for member ‘len’ in something not a structure or union.

Затем я попытался:

int len = ntohs(((record_t*)buffer)->len);

чтобы получить право на приоритет оператора. Это дало мне warning: dereferencing type-punned pointer will break strict-aliasing rules.

то я объявил

record_t *rec = null;

rec = (record_t*)

что я здесь делаю неправильно?

4b9b3361

Ответ 1

В соответствии со стандартами C и С++ поведение undefined доступно для доступа к переменной данного типа с помощью указателя на другой тип. Пример:

int a;
float * p = (float*)&a;  // #1
float b = *p;            // #2

Здесь # 2 вызывает поведение undefined. Назначение в # 1 называется "тип punning". Термин "псевдонижение" относится к идее о том, что несколько разных переменных указателя могут указывать на одни и те же данные - в этом случае p псевдонизирует данные a. Правовое сглаживание является проблемой для оптимизации (что является одной из основных причин превосходной производительности Fortran в определенных ситуациях), но то, что мы имеем здесь, - это незаконное наложение псевдонимов.

Ваша ситуация ничем не отличается; вы получаете доступ к данным в buffer с помощью указателя на другой тип (т.е. указатель, который не является char *). Это просто не разрешено.

Результат: у вас никогда не должно быть данных в buffer.

Но как его решить? Убедитесь, что у вас есть действующий указатель! Существует одно исключение для типа punning, а именно для доступа к данным через указатель на char, который разрешен. Поэтому мы можем написать это:

record_t data;
record_t * p = &data;          // good pointer
char * buffer = (char*)&data;  // this is allowed!

return p->len;                 // access through correct pointer!

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

Ответ 2

Вы получаете это предупреждение, потому что вы нарушаете строгое сглаживание, имея два указателя разных типов, указывающих на одно и то же местоположение.

Один из способов обойти это - использовать союзы:

union{
    unsigned char buffer[24];
    record_t record_part;
};

//code to fill the buffer with the relavent information.

int len = ntohs(record_part.len);

EDIT:

Строго говоря, это не намного безопаснее, чем исходный код, но он не нарушает строгое сглаживание.

Ответ 3

Вы можете попробовать следующее:

unsigned char buffer[sizeof(record_t)];
record_t rec;
int len;

// code to fill in buffer goes here...

memcpy(&rec, buffer, sizeof(rec));
len = ntohs(rec.len);

Ответ 4

У вас, вероятно, есть набор уровней предупреждения, который включает в себя строгие предупреждения о псевдониме (он не использовался по умолчанию, но в какой-то момент gcc перевернул значение по умолчанию). попробуйте -Wno-strict-aliasing или -fno-strict-aliasing - тогда gcc не должен генерировать предупреждения

Достаточно хорошее объяснение (на основе беглого взгляда) Что такое строгое правило псевдонимов?