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

这是gcc 6.2中的存储重用(别名)错误吗?,gcc,strict-aliasing,Gcc,Strict Aliasing,据我所知,对于定义int64_t的平台,无论long是否被识别为别名兼容,以下代码在任何合理的标准阅读下都应该具有100%的定义行为,并且long具有相同的大小和表示形式 #include <stdint.h> #include <string.h> #include <stdlib.h> #include <stdio.h> typedef long long T1; typedef int64_t T2; #define T1FMT "%ll

据我所知,对于定义
int64_t
的平台,无论
long
是否被识别为别名兼容,以下代码在任何合理的标准阅读下都应该具有100%的定义行为,并且
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;      
}
#包括
#包括
#包括
#包括
typedef长T1;
typedef int64_t T2;
#定义T1FMT“%lld”
#定义T11值
#定义T22值
T1 blah3(无效*p1,无效*p2)
{
T1*t1p,*t1p2;
T2*t2p;
T1温度;
t1p=p1;
t2p=p2;
*t1p=T1VALUE;//写入为T1
*t2p=T2VALUE;//写入为T2
temp=*t2p;//读作T2
t1p2=(T1*)t2p;//可见T2到T1指针的转换
*t1p2=temp;//写入为T1
return*t1p;//读作T1
}
T1测试3(无效)
{
void*p=malloc(sizeof(T1)+sizeof(T2));
T1结果=blah3(p,p);
自由基(p);
返回结果;
}
内部主(空)
{
T1结果=test3();
printf(“结果为“T1FMT”\n”,result);
返回0;
}
参见第页的代码(GCC 6.2 x86-64使用
-std=c99-x c-O2

测试3的正确代码应该分配一些存储空间,然后:

  • 写入值为1的
    long
  • 通过写入值为2的
    int64\u t
    ,将存储器的有效类型设置为
    int64\u t
  • 将存储读取为
    int64\u t
    (其有效类型),应产生2
  • 通过使用上述值(应为2)存储a
    long-long
    ,将存储的有效类型设置为
    long
  • 将存储读取为type
    long
    ,这将产生2
  • 然而,戈德博尔特现场的gcc x86-64 6.2不产生2;相反,它产生1。我没有找到gcc行为的任何其他类型组合 这样地。我认为发生的事情是,gcc决定可以省略*t1p2的存储,因为它没有效果,但它没有认识到该存储确实具有将存储的有效类型从
    int64\t
    更改为
    long
    的效果

    虽然我认为不承认<代码> ITS64×T< <代码>和<代码>长Lo/<代码>作为别名兼容,但我看不出标准中的任何东西都能证明GCC在存储值1之后无法识别存储的重用以保持值2。除了编写时使用的类型外,任何类型都不能被读取,但我认为gcc决定传递给“blah”的两个指针不能使用别名


    我是否遗漏了什么,或者这是一个彻底的错误?

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

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


    但是,通过本地安装GCC4.9.2(x86_64-win32-seh-rev1),我得到了正确的输出2。因此,问题似乎只存在于gcc的某些版本中。

    据我所知,出现此错误的一个关键条件是编译器必须(错误地)将
    *t1p2
    的存储视为不可操作。我不知道是否所有gcc安装都对
    int64\t
    使用相同的定义,由于在将该类型指定为
    long
    的平台上,此处的代码可以正常工作,如图所示,但如果将T1更改为
    long
    ,则代码将失败。我想知道该标准的作者是否曾想过,任何高质量的编译器都不会识别具有相同大小和表示形式的类型之间的别名,因为无法识别别名可能会使在使用不同命名类型的API之间以定义的方式交换数据变得非常困难?在任何情况下,我认为发生的事情是gcc决定写入为
    T2
    不会因为其类型而影响
    *t1p
    ,而写入为
    T1
    不会影响它,因为它只是写入了“已经写入的内容”(即使之前的写入被忽略)。@supercat似乎是合理的,我想这可以通过深入研究优化器代码来证实