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


25

考虑以下结构:

struct s {
  int a, b;
};

通常为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);

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

o2.a=3
o2.b=4
o1.a=1
o1.b=3

特别是,我想知道编写o1o2重叠的对象所指向的对象发生了什么。是否仍然可以访问未打通的部分(o1->a)?访问被破坏的部分o1->b与访问完全一样o2->a吗?

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

如果第二次写入的类型不同,会有什么变化吗?如果委员们说,intshort而不是两个intS'

如果您想在这里玩,这里有一个螺栓


1此答案适用于并非如此的平台:例如,某些平台的尺寸和对齐方式可能为4。在尺寸和对齐方式相同的平台上,此问题将不适用,因为对齐的重叠对象会不可能,但是我不确定是否有这样的平台。


2
我很确定这是UB,但我会让语言律师提供章节和经文。
Barmar

我认为旧的Cray向量系统上的C编译器使用ILP64模型并强制进行64位对齐(地址是64位字-无字节寻址),因此其对齐方式和大小必须相同。当然,这产生大量的其他问题....的
约翰·d McCalpin

Answers:


15

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

我们一直在努力写出更好的规则,但是到目前为止,它们还没有形成任何规范性的文本,我不确定C2x的现状如何。

正如我在回答你刚才的问题中提到,最常见的解释是,p->q手段(*p).q有效的类型适用于所有的*p,即使我们之后再去申请.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);
}

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

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


1
使用f(s* s1, s* s2),,时restrict,编译器无法假定s1并且s2是不同的指针。我认为,如果再没有restrict,它甚至不能假设它们没有部分重叠。IAC,我认为f()类推并不能很好地说明OP的担忧。祝你好运。上半年紫外线。
chux-恢复莫妮卡

s1 == s2可以无限制使用@ chux-ReinstateMonica ,但不能部分重叠。(如果,我的代码示例中的优化仍然可以执行s1 == s2
MM

@ chux-ReinstateMonica您也可以考虑使用相同的问题,int而不是使用structs(以及使用的系统_Alignof(int) < sizeof(int))。
MM

3
有关C2x有效类型的此类问题的状态是相当开放的,尚需在研究小组中进行辩论。声明与p->q和等价时要小心(*p).q。您声明的类型解释可能是正确的,但从操作的角度来看,事实并非如此。对于并发访问同一结构很重要,一个成员的访问并不意味着任何其他成员的访问。
詹斯·古斯特

严格的别名规则是关于访问的。表达式中的左侧E1.E2表达式不执行访问(我指的是整个E1表达式。它的某些子表达式可以执行访问。即,如果E1is (*p),则在评估时读取指针值p是access,但是对*por的评估或(*p)不执行任何操作访问)。严格的别名规则不适用于没有访问权限的情况。
语言律师
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.