C中重叠对象的语义是什么?

Bee*_*ope 25 c struct pointers language-lawyer c11

考虑以下结构:

struct s {
  int a, b;
};
Run Code Online (Sandbox Code Playgroud)

通常为1,此结构的大小为 8,对齐方式为 4。

如果我们创建两个struct s对象(更准确地说,我们将两个这样的对象写入分配的存储空间),并且第二个对象与第一个对象重叠会怎样?

char *storage = malloc(3 * sizeof(struct s));
struct s *o1 = (struct s *)storage; // offset 0
struct s *o2 = (struct s *)(storage + alignof(struct s)); // offset 4

// now, o2 points half way into o1
*o1 = (struct s){1, 2};
*o2 = (struct s){3, 4};

printf("o2.a=%d\n", o2->a);
printf("o2.b=%d\n", o2->b);
printf("o1.a=%d\n", o1->a);
printf("o1.b=%d\n", o1->b);
Run Code Online (Sandbox Code Playgroud)

这个程序的未定义行为有什么问题吗?如果是这样,它在哪里变得未定义?如果不是UB,是否保证始终打印以下内容:

o2.a=3
o2.b=4
o1.a=1
o1.b=3
Run Code Online (Sandbox Code Playgroud)

特别是,我想知道o1when指向的对象发生了什么o2,它与它重叠,被写入。是否仍然允许访问未破坏的部分 ( o1->a)?访问被破坏的部分是否与访问o1->b相同o2->a

有效类型在这里如何应用?当您谈论非重叠对象和指向与最后一个存储相同位置的指针时,规则就足够清楚了,但是当您开始谈论对象或重叠对象部分的有效类型时,规则就不太清楚了。

如果第二次写入的类型不同,会有什么变化吗?如果成员是 say intandshort而不是两个ints?

如果你想在那里玩,这里有一个神马


1这个答案也适用于不是这种情况的平台:例如,有些可能有大小 4 和对齐方式 2。在大小和对齐方式相同的平台上,这个问题不适用,因为对齐、重叠的对象会不可能,但我不确定是否有这样的平台。

M.M*_*M.M 15

基本上这是标准中的所有灰色区域;严格的别名规则指定了基本情况,让读者(和编译器供应商)填写细节。

一直在努力编写更好的规则,但到目前为止还没有产生任何规范文本,我不确定 C2x 的状态如何。

正如我在对您上一个问题的回答中所述,最常见的解释是p->qmean(*p).q有效类型适用于所有*p,即使我们随后继续 apply .q

在这种解释下,printf("o1.a=%d\n", o1->a);会导致未定义的行为,因为该位置的有效类型*o1不是s(因为它的一部分已被覆盖)。

这种解释的基本原理可以在如下函数中看到:

void f(s* s1, s* s2)
{
    s2->a = 5;
    s1->b = 6;
    printf("%d\n", s2->a);
}
Run Code Online (Sandbox Code Playgroud)

通过这种解释,最后一行可以优化为puts("5");,但如果没有它,编译器将不得不考虑函数调用可能已经存在f(o1, o2);,因此失去了据称由严格别名规则提供的所有好处。

类似的论点适用于两个不相关的结构类型,它们碰巧int在不同的偏移处有一个成员。

  • 这种关于 C2x 有效类型的问题的状态几乎是开放的,并且仍然在研究组中存在争议。但在声明“p->q”和“(*p).q”等价时要小心。对于您所说的类型解释来说,这可能是正确的,但从操作的角度来看,情况并非如此。对于对同一结构的并发访问来说,重要的是成员的访问并不意味着任何其他成员的访问。 (3认同)