Разглаживающий тип-караульный указатель нарушит правила строгого сглаживания
У меня есть указатель без знака 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*)
что я здесь делаю неправильно?
Ответы
Ответ 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 не должен генерировать предупреждения
Достаточно хорошее объяснение (на основе беглого взгляда) Что такое строгое правило псевдонимов?