c cast和deference指针严格别名

时间:2016-07-24 12:22:03

标签: c11 strict-aliasing

http://blog.regehr.org/archives/1307中,作者声称以下代码段具有未定义的行为:

unsigned long bogus_conversion(double d) {
  unsigned long *lp = (unsigned long *)&d;
  return *lp;
}

该参数基于http://port70.net/~nsz/c/c11/n1570.html#6.5p7,它指定了允许的访问环境。但是,在这个项目符号的脚注(88)中,它说这个列表仅用于检查别名,所以我认为这个片段很好,假设为sizeof(long) == sizeof(double)

我的问题是上述代码段是否被允许。

1 个答案:

答案 0 :(得分:0)

该代码段是错误的,但不是因为别名。首先,有一个简单的规则,即使用与其有效类型不同的类型来指向对象的指针是错误的。这里的有效类型为double,因此存在错误。 标准中存在此保护措施,因为double的位表示不能是unsigned long的有效表示,尽管现在这种情况非常奇特。

其次,从更实际的角度来看,doubleunsigned long可能具有不同的对齐属性,以这种方式访问​​它可能会产生总线错误或只是运行时间损失。< / p>

一般来说,像这样的铸造指针几乎总是错误的,没有明确的行为,是不好的风格,而且无论如何都是无用的。在关于这些问题的论证中关注别名是一个坏习惯,可能源于难以理解和可怕的gcc警告。

如果你真的想知道某种类型的位表示,有一些例外的&#34;有效类型&#34;规则。有两种便携式解决方案由C标准明确定义:

  • 使用unsigned char*并检查字节。
  • 使用包含两种类型的union,将值存储在其中并使用其他类型读取。通过这个你告诉编译器你想要一个可以看作两种类型的对象。但是在这里你不应该使用unsigned long作为目标类型,而是使用uint64_t,因为你必须确保大小正是你所想的那样,并且没有陷阱表示。

为了说明这一点,这里的功能与问题中的功能相同,但具有已定义的行为。

unsigned long valid_conversion(double d) {
  union {
    unsigned long ul;
    double d;
  } ub = { .d = d, };
  return ub.ul;
}

我的编译器(在Debian上的gcc,没有什么花哨的)将这个编译成与问题中的代码完全相同的汇编程序。只有你知道这段代码是可移植的。