c - 严格别名规则背后的基本原理是什么?
问题描述
我目前想知道严格别名规则背后的基本原理。我知道在 C 中不允许某些别名,其目的是允许优化,但令我惊讶的是,在定义标准时,这是跟踪类型转换的首选解决方案。
因此,显然以下示例违反了严格的别名规则:
uint64_t swap(uint64_t val)
{
uint64_t copy = val;
uint32_t *ptr = (uint32_t*)© // strict aliasing violation
uint32_t tmp = ptr[0];
ptr[0] = ptr[1];
ptr[1] = tmp;
return copy;
}
我可能是错的,但据我所知,编译器应该能够完美而轻松地追踪类型转换并避免对显式转换的类型进行优化(就像它避免对同一类型指针进行此类优化一样)任何东西用受影响的值调用。
那么,我错过了哪些严格别名规则的问题,编译器无法轻松解决以自动检测可能的优化)?
解决方案
由于在本例中,所有代码对编译器都是可见的,因此编译器可以假设性地确定所请求的内容并生成所需的汇编代码。然而,证明理论上不需要严格的别名规则的一种情况并不能证明不需要它的其他情况。
考虑代码是否包含:
foo(&val, ptr)
的声明在foo
哪里void foo(uint64_t *a, uint32_t *b);
。然后,foo
可能在另一个翻译单元中的 inside ,编译器将无法知道这一点a
并b
指向同一对象的(部分)。
那么有两种选择:一是语言可能允许别名,在这种情况下编译器在翻译时不能根据和不同foo
的事实进行优化。例如,每当向 写入内容时,编译器必须生成汇编代码以重新加载,因为它可能已经更改。不允许进行优化,例如在使用它时保留寄存器中的副本。*a
*b
*b
*a
*a
第二个选择,二,是禁止别名(特别是,如果程序这样做,则不定义行为)。*a
在这种情况下,编译器可以根据和*b
不同的事实进行优化。
C 委员会选择了选项二,因为它提供了更好的性能,同时不会过度限制程序员。
推荐阅读
- angular - 如何在 Angular 6 中使用带有查询参数的“routerLinkActive”?
- android - 如何保存嵌套的recyclerview单项数据?
- php - 如何从请求中获得加号?
- csv - 如何将非常大的 csv 文件与另一个文件连接起来?
- android - 我可以在哪里获得 google-services.json 而无需迁移到 Firebase?
- windows - VS Code 和 Windows Git 中的文件名区分大小写
- powerbi - 在 Power BI 中减去两个日期/时间列
- javascript - Ember.js 中的简单 `yield`
- r - 计算数据框中数值和分类变量的百分比
- java - Firebase Firestore 分布式计数器 - 找不到要序列化的属性