-Werror=C 中的严格别名错误

Posted

技术标签:

【中文标题】-Werror=C 中的严格别名错误【英文标题】:-Werror=strict-aliasing error in C 【发布时间】:2018-03-06 19:12:32 【问题描述】:

我正在尝试调用这个函数

static inline void insert(Buffer *buf, double f, size_t index)

   insert_64(buf, *(uint64_t *)&f, index);

但是,我收到了这个错误:

error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]

不知道我哪里错了。

【问题讨论】:

*(uint64_t *)&f 那里。你不能假装指向float 的指针是指向uint64_t 的指针并侥幸逃脱。 Fix for dereferencing type-punned pointer will break strict-aliasing的可能重复 @LeeDanielCrocker 取消引用新指针是 UB,OP 肯定做错了什么。 看这里:What is the strict aliasing rule? 不,这不仅仅是关于便携性。打破严格的别名规则以及一些优化可能会导致更有趣的东西。看这里:blog.qt.io/blog/2011/06/10/type-punning-and-strict-aliasing 【参考方案1】:

不知道我哪里错了。

*(uint64_t *)&f 打破了严格的别名规则。见Fix for dereferencing type-punned pointer will break strict-aliasing

要修复,请使用memcpy() 以及@dbush 或union 的回答。 union 处理严格的别名规则。

void insert2(Buffer *buf, double f, size_t index) 
  union  
    double d; 
    uint64_t u64;
   u =  f ;
  insert_64(buf, u.u64, index);

或者也使用一个复合文字(从C99开始可用)

void insert3(Buffer *buf, double f, size_t index) 
  //             v-----------------------------------------v   compound literal        
  insert_64(buf, (union  double d; uint64_t u64; ).d = f.u64, index);


对于 C11,我将添加以下内容以供将来评估正确性。

_Static_assert(sizeof (double) == sizeof (uint64_t), "Unexpected sizes");

注意:稀有平台的每个整数和 FP 类型都有不同的 endian

【讨论】:

【参考方案2】:

正如 cmets 中提到的,尝试将指向一种类型的指针转​​换为另一种类型的指针会破坏严格的别名规则。您可以通过使用memcpy 将字节从一种类型复制到另一种类型来解决此问题:

uint64_t x;
memcpy(&x, &f, sizeof(f));

注意x 包含的内容取决于实现,即double 的表示形式是什么,是系统大端还是小端,是double 64 位等。

【讨论】:

以上是关于-Werror=C 中的严格别名错误的主要内容,如果未能解决你的问题,请参考以下文章

_Bool类型和严格别名

C++ 构建警告:取消引用类型双关指针将破坏严格别名规则

c++ 模板化抽象基类数组,不违反严格别名规则

C++ 严格别名:这不是 MSFT 示例 UB 吗? [复制]

在 VC++ 11 中使用别名声明

错误 #1066 - MySQL 中的表/别名不是唯一的