这是gcc 6.2中的存储重用(别名)错误吗?

时间:2016-08-30 22:26:55

标签: gcc strict-aliasing

据我所知,以下代码应该在定义int64_t的平台的标准的任何合理读数下具有100%定义的行为,并且long long具有相同的大小和表示,无论如何是否long long被识别为别名兼容。

#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

typedef long long T1;
typedef int64_t T2;
#define T1FMT "%lld"
#define T1VALUE 1
#define T2VALUE 2

T1 blah3(void *p1, void *p2)
{
  T1 *t1p, *t1p2;
  T2 *t2p;
  T1 temp;

  t1p = p1;
  t2p = p2;
  *t1p = T1VALUE;   // Write as T1
  *t2p = T2VALUE;   // Write as T2
  temp = *t2p;      // Read as T2
  t1p2 = (T1*)t2p;  // Visible T2 to T1 pointer conversion
  *t1p2 = temp;     // Write as T1
  return *t1p;      // Read as T1
}

T1 test3(void)
{
  void *p = malloc(sizeof (T1) + sizeof (T2));
  T1 result = blah3(p,p);
  free(p);
  return result;
}
int main(void)
{
  T1 result = test3();
  printf("The result is " T1FMT "\n", result);
  return 0;      
}

请参阅https://godbolt.org/g/75oLGx上的代码(GCC 6.2 x86-64,使用-std=c99 -x c -O2

test3的正确代码应分配一些存储空间,然后:

  1. 写入值{1}的long long
  2. 通过编写值为{2的int64_t,将存储的有效类型设置为int64_t
  3. 将存储读取为int64_t(其有效类型),应该产生2
  4. 通过存储具有上述值(应为2)的a long long,将存储的有效类型设置为long long
  5. 将存储空间读取为类型long long,应该产生2。
  6. 然而,Godbolt网站上的gcc x86-64 6.2不会产生2;相反,它产生1.我没有找到gcc表现的任何其他类型的组合 像这样。我认为正在发生的事情是gcc决定可以省略* t1p2的商店,因为它没有效果,但它没有认识到商店确实具有从int64_t更改存储的有效类型的效果到long long

    虽然我认为不承认int64_tlong long是别名兼容的决定值得怀疑,但我认为标准中没有任何内容可以证明gcc未能识别存储的重用以保持在它之前保持值为1之后的值为2.除了写入它之外的任何类型都没有读取任何类型,但我认为gcc决定传递给“blah”的两个指针不能别名。

    我错过了什么或者是一个彻头彻尾的错误吗?

1 个答案:

答案 0 :(得分:0)

正如您所解释的,代码不违反严格别名规则。事实上,T1T2可以是任何类型(这样的分配不是类型不匹配),它们不需要具有相同的大小或任何东西。

我同意输出1是一个编译器错误。在godbolt网站上,所有版本的gcc似乎都有bug,而clang则提供正确的输出。

但是,使用gcc 4.9.2(x86_64-win32-seh-rev1)的本地安装,我得到2的正确输出。所以看起来这个问题只存在于gcc的某些版本中。