"解除引用类型惩罚指针将破坏严格别名规则"警告

pet*_*ohn 53 c++ warnings strict-aliasing

我使用一个代码,我将枚举*转换为int*.像这样的东西:

enum foo { ... }
...
foo foobar;
int *pi = reinterpret_cast<int*>(&foobar);
Run Code Online (Sandbox Code Playgroud)

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

dereferencing type-punned pointer will break strict-aliasing rules
Run Code Online (Sandbox Code Playgroud)

我用Google搜索了这条消息,发现只有在严格的别名优化打开时才会发生这种情况.我有以下问题:

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

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

moo*_*dow 57

为了:

  • 是.GCC将假设指针不能别名.例如,如果你通过一个分配,然后从另一个读取,GCC可以作为优化,重新排序读取和写入 - 我已经看到这发生在生产代码中,并且调试不愉快.

  • 一些.您可以使用联合来表示需要重新解释的内存.你可以用一个reinterpret_cast.您可以char *在重新解释内存的位置强制转换 - char *定义为能够为别名设置别名.你可以使用一个类型__attribute__((__may_alias__)).您可以使用-fno-strict-aliasing全局关闭别名假设.

  • __attribute__((__may_alias__)) 对于使用的类型,可能是最接近禁用特定代码段的假设.

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

  • "通过`char*`施放" - 你的观点没有错,但"通过"可能会产生误导.你可以通过`char*`或`unsigned char*`逐个读取字节.但是`reinterpret_cast <int*>(reinterpret_cast <char*>(&foobar))`仍然会打破严格的别名,即使我已经"通过`char*`强制转换"了.如果X可以别名为Y,并且Y可以别名为Z(Y是`char*`),那么在严格的别名规则下,它不一定遵循X可以别名Z. (17认同)
  • 只是一个警告,根据C++标准,通过写入一个成员并从另一个成员读取来使用union是未指定的行为.所以它可能有效,但可能没有. (12认同)
  • @JPvdMerwe:GCC明确支持它.MSVC也是如此. (4认同)
  • @JPvdMerwe:查看已接受的答案http://stackoverflow.com/questions/8511676/portable-data-reinterpretation (2认同)

Cas*_*Cow 11

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

所以,是的,它可能会产生"潜在的"错误代码.

  • @petersohn也许在你的机器上,这并不意味着什么. (5认同)
  • @petersohn:现在怎么样?它们的尺寸仍然相同吗?你不能保证只因为它在某一点上起作用,它就会*保持*工作. (4认同)
  • @jalf:是的,它们的大小仍然相同。它是实现定义的枚举的基础类型是什么,而不是未指定。我实际上并没有检查过 GCC 关于这个主题的文档,但我很确定他们不会说,“周一、周三和周五是完整的,但周二、周四、周末和公共假期是短暂的”。所以我强烈怀疑 petersohn 实际上确实有一个保证,对于他说他正在使用的编译器,可能会因平台而异。只是没有人费心去查阅和引用它。 (2认同)

小智 11

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

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;
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

unsigned int data = alias_cast<unsigned int>(raw_ptr);
Run Code Online (Sandbox Code Playgroud)

  • 添加`static_assert(sizeof(T)== sizeof(F),"不能转换不同大小的类型");`to`alias_cast`将提高此代码的安全性. (7认同)

ice*_*ime 5

你有没有看过这个答案

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