53

我使用了一个代码,将 enum* 转换为 int*。像这样的东西:

enum foo { ... }
...
foo foobar;
int *pi = reinterpret_cast<int*>(&foobar);

编译代码(g++ 4.1.2)时,我收到以下警告消息:

dereferencing type-punned pointer will break strict-aliasing rules

我用谷歌搜索了这条消息,发现只有在启用严格别名优化时才会发生这种情况。我有以下问题:

  • 如果我留下这个警告的代码,它会产生潜在的错误代码吗?
  • 有没有办法解决这个问题?
  • 如果没有,是否可以从源文件内部关闭严格别名(因为我不想为所有源文件关闭它,也不想为此源文件制定单独的 Makefile 规则)?

是的,我实际上需要这种别名。

4

5 回答 5

59

为了:

  • 是的。GCC 将假定指针不能别名。例如,如果您通过一个分配然后从另一个读取,作为优化,GCC 可能会重新排序读取和写入 - 我在生产代码中看到过这种情况,调试起来并不愉快。

  • 一些。您可以使用联合来表示您需要重新解释的内存。你可以使用一个reinterpret_cast. 您可以char *在重新解释内存的地方进行转换 -char *被定义为能够为任何东西起别名。您可以使用具有__attribute__((__may_alias__)). 您可以使用 -fno-strict-aliasing 全局关闭别名假设。

  • __attribute__((__may_alias__))在使用的类型上可能是最接近禁用特定代码部分的假设的方法。

对于您的特定示例,请注意枚举的大小定义不明确;GCC 通常使用可用于表示它的最小整数大小,因此将指向枚举的指针重新解释为整数可能会在结果整数中留下未初始化的数据字节。不要那样做。为什么不直接转换为适当大的整数类型?

于 2010-11-12T09:33:03.730 回答
13

您可以使用以下代码来转换您的数据:

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

示例用法:

unsigned int data = alias_cast<unsigned int>(raw_ptr);
于 2013-11-06T08:56:50.763 回答
11

但是你为什么要这样做?如果 sizeof(foo) != sizeof(int) 它将中断。仅仅因为枚举就像一个整数并不意味着它被存储为一个。

所以是的,它可能会生成“潜在的”错误代码。

于 2010-11-12T09:29:30.117 回答
5

你看过这个答案吗?

严格的别名规则使得这个设置是非法的,两个不相关的类型不能指向同一个内存。只有 char* 有这个特权。不幸的是,你仍然可以用这种方式编码,也许会得到一些警告,但让它编译得很好。

于 2010-11-12T09:25:14.137 回答
3

严格别名是一个编译器选项,因此您需要从 makefile 中将其关闭。

是的,它会生成不正确的代码。编译器将有效地假设foobar并且pi没有绑定在一起,并且假设*pi如果改变也不会foobar改变。

如前所述,static_cast改为使用(并且没有指针)。

于 2010-11-12T09:35:17.477 回答