Does a simple cast to perform a raw copy of a variable break strict aliasing?

432 views Asked by At

I've been reading about strict aliasing quite a lot lately. The C/C++ standards say that the following code is invalid (undefined behavior to be correct), since the compiler might have the value of a cached somewhere and would not recognize that it needs to update the value when I update b;

float *a;
...
int *b = reinterpret_cast<int*>(a);
*b = 1;

The standard also says that char* can alias anything, so (correct me if I'm wrong) compiler would reload all cached values whenever a write access to a char* variable is made. Thus the following code would be correct:

float *a;
...
char *b = reinterpret_cast<char*>(a);
*b = 1;

But what about the cases when pointers are not involved at all? For example, I have the following code, and GCC throws warnings about strict aliasing at me.

float a = 2.4;
int32_t b = reinterpret_cast<int&>(a);

What I want to do is just to copy raw value of a, so strict aliasing shouldn't apply. Is there a possible problem here, or just GCC is overly cautious about that?

EDIT

I know there's a solution using memcpy, but it results in code that is much less readable, so I would like not to use that solution.

EDIT2

int32_t b = *reinterpret_cast<int*>(&a); also does not work.

SOLVED

This seems to be a bug in GCC.

3

There are 3 answers

7
Bill On BEST ANSWER

If you want to copy some memory, you could just tell the compiler to do that:

Edit: added a function for more readable code:

#include <iostream>
using std::cout; using std::endl;
#include <string.h>

template <class T, class U>
T memcpy(const U& source)
{
    T temp;
    memcpy(&temp, &source, sizeof(temp));
    return temp;
}

int main()
{
    float f = 4.2;
    cout << "f: " << f << endl;

    int i = memcpy<int>(f);
    cout << "i: " << i << endl;
}

[Code] [Updated Code]

Edit: As user/GMan correctly pointed out in the comments, a full-featured implementation could check that T and U are PODs. However, given that the name of the function is still memcpy, it might be OK to rely on your developers treating it as having the same constraints as the original memcpy. That's up to your organization. Also, use the size of the destination, not the source. (Thanks, Oli.)

5
AProgrammer On

Basically the strict aliasing rules is "it is undefined to access memory with another type than its declared one, excepted as array of characters". So, gcc isn't overcautious.

3
user470379 On

If this is something you need to do often, you can also just use a union, which IMHO is more readable than casting or memcpy for this specific purpose:

union floatIntUnion {
  float a;
  int32_t b;
};

int main() {
  floatIntUnion fiu;
  fiu.a = 2.4;
  int32_t &x = fiu.b;
  cout << x << endl;
}

I realize that this doesn't really answer your question about strict-aliasing, but I think this method makes the code look cleaner and shows your intent better.

And also realize that even doing the copies correctly, there is no guarantee that the int you get out will correspond to the same float on other platforms, so count any network/file I/O of these floats/ints out if you plan to create a cross-platform project.