5

The codebase at work contains some code that looks roughly like this:

#define DATA_LENGTH 64

u_int32 SmartKey::SerialNumber()
{
    unsigned char data[DATA_LENGTH];
    // ... initialized data buffer
    return *(u_int32*)data;
}

This code works correctly, but GCC gives the following warning:

warning: dereferencing pointer ‘serialNumber’ does break strict-aliasing rules

Can someone explain this warning? Is this code potentially dangerous? How can it be improved?

Update
With thanks to James McNellis' answer I came up with the following utility function:

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

Feel free to suggest improvements!

1
  • Probably has to do with pointer being cast from unsigned char * to u_int32 * but been a long time since I did C++. If so, since DATA_LENGTH is an exact multiple of 32 there should not be an issue. Commented May 27, 2010 at 15:27

4 Answers 4

11

The warning is because you are violating the strict aliasing rule.

One way to do it correctly would be to copy the bytes from the data buffer into a u_int32 object and return that object:

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;

This solution works because in C++ it is permitted to access any type of object as an array of char.

(std::copy() is in <algorithm>)

Sign up to request clarification or add additional context in comments.

3 Comments

Exactly what i wanted to post. Although my solution used memcpy.
Even if you are not using STL, at least use reinterpret_cast.
One subtlety that should be mentioned whenever talking about (de)serializing data in this way is that of endianness. It's less of a concern now that Apple uses Intel chips, but should be kept in mind lest your data becomes severely corrupted.
2

In C and C++ languages reinterpreting memory occupied by object of one type as an object of another type is illegal - it leads to undefined behavior. Some compilers use this rule to perform aggressive aliasing-related optimizations. As a consequence, your code might not work as expected, if you perform the aforementioned reinterpretation.

In C/C++ it is OK to reinterpret any object as an array of char, but it is not OK to take a standalone array of char and reinterpret is as an object of some other type. This is what your code is doing.

In addition to aliasing problems, you have to keep in mind that a standalone automatic char array is not guaranteed to be aligned properly to be read as an u_int32 value.

The proper way to do what the above code is trying to do is to copy the source array into an intermediate u_int32 value using memcpy

u_int32 SmartKey::SerialNumber()
{
    unsigned char data[DATA_LENGTH];
    u_int32 u;
    // ...
    memcpy(&u, data, sizeof u);
    return u;
}

Of course, you have to be sure that the endianness of the data is the same as the endianness of the u_int32 objects on your platform.

Comments

0

I think the problem is actually somewhere in your elided code to initialize the data[] structure. I don't think it has anything to do with your cast, which is fine.

Comments

-1

Not sure, but I think you can do like that:

return (u_int32)&data;

1 Comment

That will convert the address of data to u_int32. The OP wants to read the actual content stored in first elements of data. Not even remotely the same thing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.