【问题标题】:Does this break the strict aliasing rule?这是否违反了严格的别名规则?
【发布时间】:2020-04-11 12:15:10
【问题描述】:

受我对最近帖子的评论的启发:C get element on place without parenthesis,我想知道以下代码是否违反了严格的别名规则:

#include <stdio.h>

int main(void)
{
    int num[3] = { 1, 2, 3 };

    printf("num[1] = %d\n", *(int *)((char *)num + sizeof(int)));

    return 0;
}

我知道将类型转换为 char 以外的其他类型的指针取消引用是违规的,但在这里,原始指针的类型为 int *。在将其转换为 char * 然后再转换为 int * 后,它会被取消引用。

这是否违反了严格的别名规则?

【问题讨论】:

    标签: c language-lawyer pointer-arithmetic strict-aliasing


    【解决方案1】:

    引用C11,章节§6.5p7

    对象的存储值只能由具有以下类型之一的左值表达式访问:

    1. 与对象的有效类型兼容的类型

    还有

    引用C11,章节§6.5p6

    访问其存储值的对象的有效类型是对象的声明类型,如果有的话......

    强调我的

    这里num指向的对象的有效类型确实是int,因此可以使用指向int的指针取消引用它。

    【讨论】:

    • @curiousguy 我不确定您指的是哪个指针和对象。你能详细说明一下这个问题吗?
    • + sizeof(int)的作用是什么?可以这样操作指针吗?
    • @curiousguy 是的,数组的定义保证整数类型的对象与sizeof(int)隔开
    • 它们具有给定的布局是一回事,您可以对指针进行算术运算,就好像它们是整数一样是另一回事。这对我来说似乎并不明显。
    • @curiousguy 我们不是对它们作为整数进行算术运算,而是对具有明确语义的char* 指针进行算术运算。你可以在这里找到它6.5.6p8
    【解决方案2】:

    不会破坏严格的别名。

    num 被转换为char * 并对其执行算术运算后,结果指针被转换为int *,并且该指针指向一个有效的int 对象。所以它可以被安全地取消引用。

    【讨论】:

    • 什么规则(如果有)可以免除 N1570 6.5p7 中使用其元素类型的左值对数组对象进行的访问的约束?不能适应使用元素类型对数组进行访问的编译器将毫无用处,但标准中是否有任何内容实际上需要编译器有效地处理此类访问?
    • @supercat 属于数组的对象已将 type 声明为数组元素的类型。因此它们将与元素类型的左值表达式兼容。
    • @AjayBrahmakshatriya:是的,但是数组中的对象也是数组对象的一部分,因此对数组中对象的任何访问也是一种访问到数组对象。这对于独立的数组对象是不可观察的,因为不能直接使用数组类型的左值,但对于包含在结构或联合中的数组是可观察的。 clang 和 gcc 都对 aggregate.memberArray[index](*((aggregate.memberArray)+(index))) 应用了不同的别名假设,即使标准将前者定义为后者。
    • @supercat 这是一个有趣的观点。我一直认为 6.5p7 中的“访问”是左值到右值的转换,而不是实际的 * 运算符。这里的aggregate.memberArray 是int[] 类型的左值。它立即被转换为int[] 类型的右值(用于算术)。因此用于访问数组类型对象的左值是数组。我不确定
    • @AjayBrahmakshatriya:我认为解释事物的最佳方式是说形成对象地址的操作和结果指针的使用共同构成对父对象的访问,并且规则在 6.5p7 中确定此类访问可能重叠的唯一情况。这样的解释将适应大多数没有-fno-strict-aliasing 的clang 和gcc 无法处理的构造,但是clang 和gcc 的作者已经加倍拒绝处理此类代码,并会阻止任何需要它的授权。
    猜你喜欢
    • 1970-01-01
    • 2016-10-09
    • 2015-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多