结构中的指针算术和比较

Pointer arithmetics and comparison within a structure

我们有:

struct A {
  int x;
  int y;
} a;

假设:

offsetof(struct A, x) + sizeof(int) == offsetof(struct A, y)

C 标准(即 C11)是否保证 &a.x + 1 == &a.y 为真?

如果不是,有主流编译器保证吗?

此外,假设满足等式,是否可以通过(&a.x)[1]访问a.y的值而无需UB

memcpy(&a.x+1, ...)呢?


编辑

使用 (&a.x)[1] 访问 a.y 确实是 UB,至少对于 CLANG 来说是这样。 请参阅用户@NateEldredge example

是的。 C 2017 6.5.9,讨论 ==!=,说:

6 Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.

7 For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

根据第 7 段,a.xa.y 每个都充当一个 int 的数组,用于 ==.

由于offsetof(struct A, x) + sizeof(int) == offsetof(struct A, y)保证a.y在地址space中紧跟在a.x之后,&a.x+1 == &a.y满足第6段最后一个条件,那一个是一个指向一个数组对象末尾的指针,另一个是指向紧跟在第一个之后的另一个数组对象的指针。

Moreover, assuming that the equality is satisfied, can the value of a.y be accessed via (&a.x)[1] without UB?

没有。 &a.x+1 等于 &a.y并不代表 &a.y.

这在标准中没有完全或明确说明。在某些情况下,指针算法必须能够遍历内存中相邻的对象,尤其是结构成员。例如,如果我们将指向结构的指针转换为 char *,我们可以使用它来访问结构中的各个字节。我们可以用它来遍历整个结构,包括成员。然后,如果我们适当地增加它以指向某个成员并将其转换回指向该成员类型的指针,我们应该有一个指向该成员的指针。

但是,C标准是用自然语言写成的,没有完全用形式逻辑或数学的符号表示(虽然有一些),所以它是不完整的,我们并不总是确定它具体说明了什么。由于它没有告诉我们 &a.x + 1 可用于访问 a.y,因此行为未定义为遗漏。

可能与您的示例有关:ISO/IEC 9899:2018 (C17) at 6.7.2.1#21 讨论了在 struct 末尾访问未指定大小的数组:

struct s {
  int n;
  double d[];
};

// given this assumption:
assert( sizeof (struct s) >= offsetof(struct s, d) + sizeof (double) );

// the standard says that the following code may be legitimate and might not be UB,
// but it is NOT a strictly conforming code:
struct s t1;
t1.d[0] = 4.2;

前面的例子和你的很相似。所以我的猜测是:你想做的可能不是 UB(假设你使用 offsetof 来检查等),但是 你的代码不严格符合。