Разыменование указателя типа наказывается нарушением правил строгого наложения - PullRequest
13 голосов
/ 03 октября 2011

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

unsigned char buffer[24];

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

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

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

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*)

что я тут не так делаю?

Ответы [ 4 ]

19 голосов
/ 03 октября 2011

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

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

Здесь # 2 вызывает неопределенное поведение.Назначение на # 1 называется "типом наказания".Термин «псевдоним» относится к идее, что несколько различных переменных-указателей могут указывать на одни и те же данные - в этом случае 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!

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

5 голосов
/ 03 октября 2011

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

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

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 голосов
/ 03 октября 2011

Вы можете попробовать это:

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);
0 голосов
/ 03 октября 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...