以下是代码示例:
float *P
P = (float*)&P
P[0] = 0.0f
违反了严格别名规则。
对象P
具有有效类型float *
,因为这是它的声明类型。C11 6.5/6:
访问存储值的对象的有效类型是该对象的已声明类型(如果有)。
第二行执行后,表达式P[0]
表示与P
相同的内存位置。(注意:对于此解释,假设sizeof(float) == sizeof(float *)
。显然,如果这些大小不同,则情况更糟!)
执行P[0] = 0.0f
使用类型为float
的lvalue访问类型为float *
的对象。这违反了6.5/7:
一个对象只能由具有以下类型之一的lvalue表达式访问其存储的值
在这些引用中,“访问”表示“读取或写入”。所列出的“以下类型”列表不包括任何异常,可以涵盖使用float
表达式来读取float *
的情况。
这个示例有点难以理解,因为它是自我参考的。但是,它与这个更简单的示例完全相同:
float *Q;
float *P = &Q;
*P = 0.0f;
在这种情况下,
Q
的类型为
float *
,但是它通过
float
类型的左值进行了写入。
此示例演示了该规则的原理。假设不存在严格的别名规则,并且允许所有别名。然后,评估
P[0] = 0.0f
会更改
P
。在常见的实现中,这将导致
P
现在成为空指针,但我们可以轻松想象分配其他值,该值使
P
成为指向其他变量的有效指针。
在那种情况下,行
P[1] = 0.0f
必须成功地写入另一个变量。因此,编译器将无法用
memset
替换循环;这样的 memset 将更新
P[1]
的原始位置,但不会更新执行了
P[0] = ....;
后的
P[1]
的新位置。优化不允许改变程序的可观察行为。
严格别名规则的存在意味着编译器可以执行优化,例如将此循环更改为 memset,而不必担心循环的内容可能会即时更改指针。
注意。行
P = (float *)&P
也可能是对齐违规。对齐规则和严格别名规则是完全独立的规则,不应混淆。 (一些编写不良的页面尝试将严格别名解释为某种对齐要求)。