我试图了解6.5(p6)中定义的严格别名规则:



6.5(p7):



考虑以下示例:

struct test_internal_struct_t{
    int a;
    int b;
};

struct test_struct_t{
    struct test_internal_struct_t tis;
};

int main(){
    //alocated object, no declared type
    struct test_struct_t *test_struct_ptr = malloc(sizeof(*test_struct_ptr));

    //object designated by the lvalue has type int
    test_struct_ptr->tis.a = 1;

    //object designated by the lvalue has type int
    test_struct_ptr->tis.b = 2;

    //VIOLATION OF STRICT ALIASING RULE???
    struct test_internal_struct_t tis = test_struct_ptr->tis;
    return 0;
}
malloc(sizeof(*test_struct_ptr))没有声明的类型,因为它已分配,如脚注87:



通过test_struct_ptr->tis.atest_struct_ptr->tis.b访问的对象的有效类型为int。但是对象test_struct_ptr->tis自分配以来没有有效的类型。

问题: struct test_internal_struct_t tis = test_struct_ptr->tis;是否违反严格的别名? test_struct_ptr->tis指定的对象没有有效类型,但是lvalue具有struct test_internal_struct_t类型。

最佳答案

C 2018 6.5 6使用短语“通过左值存储...存储”来定义有效类型,但从不定义该短语:



因此,留给读者解释。考虑以下代码:

struct a { int x; };
struct b { int x; };

void copy(int N, struct a *A, struct b *B)
{
    for (int n = 0; n < N; ++n)
        A[n].x = B[n].x;
}

如果编译器知道各种对象A[n]不与各种对象B[n]重叠,则它可以通过在一条指令(例如AVX或其他单指令多数据[SIMD]指令)中加载多个B[n]来优化此代码。 ),并在一条指令中存储多个A[n]。 (这可能需要其他代码来处理循环片段和对齐问题。此处不涉及我们。)如果对于某些不同的A[n]->x值,某些B[n]->x可能与n引用同一对象,则编译器可能不会使用此类多元素加载和存储,因为它可能会更改程序的可观察行为。例如,如果情况是内存包含十个int,其值从0到9,且B指向0,而A指向2:

A
0 1 2 3 4 5 6 7 8 9

然后,在给定N = 4的情况下,编写的循环必须一次复制一个元素,从而产生:

0 1 0 1 0 1 6 7 8 9

如果编译器将此优化为四元素加载和存储,则可以加载0 1 2 3然后存储0 1 2 3,从而产生:

0 1 0 1 2 3 6 7 8 9

但是,C告诉我们struct astruct b是不兼容的,即使它们的布局完全相同。当XY类型不兼容时,它告诉我们X不是Y。类型系统的一个重要目的是区分对象类型。

现在考虑表达式A[n]->x = B[n]->x。在此:
  • A[n]struct a的左值。
  • 由于A[n].的左操作数,因此不会将其转换为值。
  • A[n].x指定并且是x的成员A[n]的左值。
  • 右操作数的值替换A[n].x中的值。

  • 因此,直接访问存储值的对象仅在int成员A[n].x中。左值A[n]出现在表达式中,但它不是直接用于存储值的左值。 &A[n]的有效内存类型是什么?

    如果我们仅将此内存解释为int,则对对象访问的唯一限制是所有B[n].x均为int而所有A[n].x均为int,因此部分或全部A[n].x可以访问与部分或全部相同的内存B[n].x,并且不允许编译器进行上述优化。

    这不能满足类型系统区分struct astruct b的目的,因此不能正确解释。为了实现预期的优化,必须是A[n].x存储的内存包含struct a对象,而B[n].x访问的内存包含struct b对象。

    因此,“通过左值存储…”必须包含这样的表达式,其中左值用于派生结构的成员,但它本身并不是用于访问的最终左值。

    关于c - C中严格的别名规则,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54731678/

    10-11 12:20