Кодовая база на работе содержит некоторый код, который примерно походит на это:
#define DATA_LENGTH 64
u_int32 SmartKey::SerialNumber()
{
unsigned char data[DATA_LENGTH];
// ... initialized data buffer
return *(u_int32*)data;
}
Этот код работает правильно, но GCC дает соблюдающее предупреждение:
warning: dereferencing pointer ‘serialNumber’ does break strict-aliasing rules
Кто-то может объяснить это предупреждение? Действительно ли этот код потенциально опасен? Как это может быть улучшено?
Обновление
С благодарностью к ответу James McNellis я придумал следующую служебную функцию:
template<class T, class Data>
T BinaryCast(const Data & inData)
{
T ret;
std::copy(&inData[0], &inData[0] + sizeof(ret), reinterpret_cast<char*>(&ret));
return ret;
}
u_int32 SmartKey::SerialNumber()
{
unsigned char data[DATA_LENGTH];
// ... initialized data buffer
return BinaryCast<u_int32>(data);
}
Не стесняйтесь предлагать улучшения!
Предупреждение выдается потому, что вы нарушаете правило строгого алиасинга.
Один из способов сделать это правильно - скопировать байты из буфера data
в объект u_int32
и вернуть этот объект:
unsigned char data[DATA_LENGTH];
// ... initialized data buffer
u_int32 i;
assert(sizeof (i) <= DATA_LENGTH);
std::copy(&data[0], &data[0] + sizeof (i), reinterpret_cast<char*>(&i));
return i;
Это решение работает, потому что в C++ разрешено обращаться к объектам любого типа как к массивам char
.
(std::copy()
находится в <алгоритме>
)
Не уверен , но я думаю, вы можете это сделать:
return (u_int32)&data;
В языках C и C++ переинтерпретация памяти, занятой объектом одного типа, как объекта другого типа является незаконной - это приводит к неопределенному поведению. Некоторые компиляторы используют это правило для агрессивной оптимизации, связанной с псевдонимами. Как следствие, ваш код может работать не так, как ожидалось, если вы выполните вышеупомянутую реинтерпретацию.
В C/C++ можно интерпретировать любой объект как массив char, но нельзя брать отдельный массив char и интерпретировать его как объект другого типа. Именно это и делает ваш код.
Помимо проблем с псевдонимом, вы должны помнить, что отдельный автоматический массив char не гарантированно будет выровнен должным образом, чтобы быть прочитанным как значение u_int32
.
Правильный способ сделать то, что пытается сделать приведенный выше код - скопировать исходный массив в промежуточное значение u_int32
с помощью memcpy
u_int32 SmartKey::SerialNumber()
{
unsigned char data[DATA_LENGTH];
u_int32 u;
// ...
memcpy(&u, data, sizeof u);
return u;
}
Конечно, вы должны быть уверены, что эндианальность данных совпадает с эндианальностью объектов u_int32
на вашей платформе.
Я думаю, что проблема на самом деле где-то в вашем убранном коде для инициализации структуры data[]. Я не думаю, что это имеет отношение к вашему касту, который в порядке.