【发布时间】:2020-04-06 23:06:55
【问题描述】:
这最好通过例子来解释。
typedef struct s_ {
int a, b;
} s;
int add(s* l, s* r) { return l->a + l->b + r->a + r->b; }
void init(s* v) {
v->a = 1;
v->b = 2;
}
int array_like() {
// allocate enough space for s[2]
char *p = malloc(2 * sizeof(s));
s *p1 = (s *)p;
s *p2 = (s *)(p + sizeof(s));
init(p1);
init(p2);
return add(p1, p2);
}
int array_skip() {
// allocate enough space for s[3] and init only [0] and [2]
char *p = malloc(3 * sizeof(s));
s *p1 = (s *)p;
s *p2 = (s *)(p + 2 * sizeof(s));
init(p1);
init(p2);
return add(p1, p2);
}
int half_gap() {
// allocate enough space for 2.5 s objects and lay them
// out like [s1][gap of alignof(s) bytes][s2]
char *p = malloc(2 * sizeof(s) + _Alignof(s));
s *p1 = (s *)p;
s *p2 = (s *)(p + sizeof(s) + _Alignof(s));
init(p1);
init(p2);
return add(p1, p2);
}
具体而言,考虑一个典型的平台,其中 sizeof(s) == 8 和 alignof(s) == 4 - 尽管这个问题应该同样适用于具有不同值的平台。
最后三个函数,array_like、array_skip 和 half_gap 都执行类似的功能:它们在由malloc。这三个都将第一个 s 对象放在存储的开头。它们的区别仅在于放置第二个对象的位置:
-
array_like将其直接放在第一个对象之后,即偏移量 8 处,因此对于指向区域开头的指针s* p,对象将位于p[0]和p[1]。 -
array_skip将sizeof(s)放置在第一个对象末尾之后的字节,即偏移 16,因此对于指向区域开头的指针s* p,对象将位于p[0]和p[2]。 -
half_gap将_Alignof(s)放置在第一个对象之后的字节,或第一个对象之后的 4 个字节,即从存储开始的 12 个字节。请注意,这个对象仍然在 4 个字节的边界上正确对齐,但它没有从第一个偏移 integral 个sizeof(s)个字节。您不能像前两种情况那样使用类似数组的符号来表示第二个对象的位置。
之后,每个函数写入两个对象的两个成员,然后从它们读取。
这些函数中哪些是合法的 C11,并且所有合法的函数都保证返回预期值 6?
【问题讨论】:
标签: c pointers language-lawyer c11 pointer-arithmetic