I use a code where I cast an enum* to int*. Something like this:
我使用了一个代码,在其中我将枚举*转换为int*。是这样的:
enum foo { ... }
...
foo foobar;
int *pi = reinterpret_cast<int*>(&foobar);
When compiling the code (g++ 4.1.2), I get the following warning message:
在编译代码(g++ 4.1.2)时,我得到以下警告信息:
dereferencing type-punned pointer will break strict-aliasing rules
I googled this message, and found that it happens only when strict aliasing optimization is on. I have the following questions:
我在google上搜索了这个消息,发现只有在严格的别名优化时才会发生。我有以下几个问题:
- If I leave the code with this warning, will it generate potentially wrong code?
- 如果我用这个警告代码离开代码,它会生成可能错误的代码吗?
- Is there any way to work around this problem?
- 有没有办法解决这个问题?
- If there isn't, is it possible to turn off strict aliasing from inside the source file (because I don't want to turn it off for all source files and I don't want to make a separate Makefile rule for this source file)?
- 如果没有,是否有可能关闭源文件中的严格别名(因为我不想把它关闭到所有源文件,而我不想为这个源文件创建一个单独的Makefile规则)?
And yes, I actually need this kind of aliasing.
是的,我确实需要这种混叠。
5 个解决方案
#1
53
In order:
在顺序:
-
Yes. GCC will assume that the pointers cannot alias. For instance, if you assign through one then read from the other, GCC may, as an optimisation, reorder the read and write - I have seen this happen in production code, and it is not pleasant to debug.
是的。GCC会假定指针不能混叠。例如,如果您通过一个然后读取另一个,GCC可以作为一个优化,重新排序读和写——我已经在生产代码中看到过这种情况,而且调试起来也不愉快。
-
Several. You could use a union to represent the memory you need to reinterpret. You could use a
reinterpret_cast
. You could cast viachar *
at the point where you reinterpret the memory -char *
are defined as being able to alias anything. You could use a type which has__attribute__((__may_alias__))
. You could turn off the aliasing assumptions globally using -fno-strict-aliasing.几个。您可以使用一个联合来表示需要重新解释的内存。您可以使用reviewt_cast。您可以通过char *进行转换,在您重新解释内存- char *的时候,定义为能够将任何内容都混为一个。您可以使用具有__attribute__((__may_alias__))的类型。你可以在全球范围内使用-fno-严格-混叠的方法关闭混叠假设。
-
__attribute__((__may_alias__))
on the types used is probably the closest you can get to disabling the assumption for a particular section of code.__attribute__((__may_alias__))对于所使用的类型可能是最接近于禁用特定代码段的假设。
For your particular example, note that the size of an enum is ill defined; GCC generally uses the smallest integer size that can be used to represent it, so reinterpreting a pointer to an enum as an integer could leave you with uninitialised data bytes in the resulting integer. Don't do that. Why not just cast to a suitably large integer type?
对于您的特定示例,请注意,枚举的大小是不确定的;GCC通常使用可以用来表示它的最小的整数大小,所以将一个指针重新解释为一个整数可以让你在生成的整数中保留未初始化的数据字节。不要这样做。为什么不把它转换成一个合适的大整数类型呢?
#2
10
But why are you doing this? It will break if sizeof(foo) != sizeof(int). Just because an enum is like an integer does not mean it is stored as one.
但你为什么要这么做?如果sizeof(foo) != sizeof(int),它将中断。仅仅因为enum就像一个整数,并不意味着它被存储为一个整数。
So yes, it could generate "potentially" wrong code.
因此,是的,它可能产生“潜在的”错误代码。
#3
9
You could use the following code to cast your data:
您可以使用以下代码来转换您的数据:
template<typename T, typename F>
struct alias_cast_t
{
union
{
F raw;
T data;
};
};
template<typename T, typename F>
T alias_cast(F raw_data)
{
alias_cast_t<T, F> ac;
ac.raw = raw_data;
return ac.data;
}
Example usage:
使用示例:
unsigned int data = alias_cast<unsigned int>(raw_ptr);
#4
5
Have you looked into this answer ?
你调查过这个答案吗?
The strict aliasing rule makes this setup illegal, two unrelated types can't point to the same memory. Only char* has this privilege. Unfortunately you can still code this way, maybe get some warnings, but have it compile fine.
严格的别名规则使这个设置非法,两个不相关的类型不能指向相同的内存。只有char*有此特权。不幸的是,您仍然可以通过这样的方式编写代码,可能会得到一些警告,但是可以将其编译好。
#5
2
Strict aliasing is a compiler option, so you need to turn it off from the makefile.
严格的别名是一个编译器选项,所以您需要将它从makefile中关闭。
And yes, it can generate incorrect code. The compiler will effectively assume that foobar
and pi
aren't bound together, and will assume that *pi
won't change if foobar
changed.
是的,它可以生成不正确的代码。编译器将有效地假设foobar和pi不绑定在一起,并且假定如果foobar发生变化,*pi不会改变。
As already mentioned, use static_cast
instead (and no pointers).
如前所述,使用static_cast(而不是指针)。
#1
53
In order:
在顺序:
-
Yes. GCC will assume that the pointers cannot alias. For instance, if you assign through one then read from the other, GCC may, as an optimisation, reorder the read and write - I have seen this happen in production code, and it is not pleasant to debug.
是的。GCC会假定指针不能混叠。例如,如果您通过一个然后读取另一个,GCC可以作为一个优化,重新排序读和写——我已经在生产代码中看到过这种情况,而且调试起来也不愉快。
-
Several. You could use a union to represent the memory you need to reinterpret. You could use a
reinterpret_cast
. You could cast viachar *
at the point where you reinterpret the memory -char *
are defined as being able to alias anything. You could use a type which has__attribute__((__may_alias__))
. You could turn off the aliasing assumptions globally using -fno-strict-aliasing.几个。您可以使用一个联合来表示需要重新解释的内存。您可以使用reviewt_cast。您可以通过char *进行转换,在您重新解释内存- char *的时候,定义为能够将任何内容都混为一个。您可以使用具有__attribute__((__may_alias__))的类型。你可以在全球范围内使用-fno-严格-混叠的方法关闭混叠假设。
-
__attribute__((__may_alias__))
on the types used is probably the closest you can get to disabling the assumption for a particular section of code.__attribute__((__may_alias__))对于所使用的类型可能是最接近于禁用特定代码段的假设。
For your particular example, note that the size of an enum is ill defined; GCC generally uses the smallest integer size that can be used to represent it, so reinterpreting a pointer to an enum as an integer could leave you with uninitialised data bytes in the resulting integer. Don't do that. Why not just cast to a suitably large integer type?
对于您的特定示例,请注意,枚举的大小是不确定的;GCC通常使用可以用来表示它的最小的整数大小,所以将一个指针重新解释为一个整数可以让你在生成的整数中保留未初始化的数据字节。不要这样做。为什么不把它转换成一个合适的大整数类型呢?
#2
10
But why are you doing this? It will break if sizeof(foo) != sizeof(int). Just because an enum is like an integer does not mean it is stored as one.
但你为什么要这么做?如果sizeof(foo) != sizeof(int),它将中断。仅仅因为enum就像一个整数,并不意味着它被存储为一个整数。
So yes, it could generate "potentially" wrong code.
因此,是的,它可能产生“潜在的”错误代码。
#3
9
You could use the following code to cast your data:
您可以使用以下代码来转换您的数据:
template<typename T, typename F>
struct alias_cast_t
{
union
{
F raw;
T data;
};
};
template<typename T, typename F>
T alias_cast(F raw_data)
{
alias_cast_t<T, F> ac;
ac.raw = raw_data;
return ac.data;
}
Example usage:
使用示例:
unsigned int data = alias_cast<unsigned int>(raw_ptr);
#4
5
Have you looked into this answer ?
你调查过这个答案吗?
The strict aliasing rule makes this setup illegal, two unrelated types can't point to the same memory. Only char* has this privilege. Unfortunately you can still code this way, maybe get some warnings, but have it compile fine.
严格的别名规则使这个设置非法,两个不相关的类型不能指向相同的内存。只有char*有此特权。不幸的是,您仍然可以通过这样的方式编写代码,可能会得到一些警告,但是可以将其编译好。
#5
2
Strict aliasing is a compiler option, so you need to turn it off from the makefile.
严格的别名是一个编译器选项,所以您需要将它从makefile中关闭。
And yes, it can generate incorrect code. The compiler will effectively assume that foobar
and pi
aren't bound together, and will assume that *pi
won't change if foobar
changed.
是的,它可以生成不正确的代码。编译器将有效地假设foobar和pi不绑定在一起,并且假定如果foobar发生变化,*pi不会改变。
As already mentioned, use static_cast
instead (and no pointers).
如前所述,使用static_cast(而不是指针)。