通过一个很好定义的void指针进行指针转换?

jxh*_*jxh 17 c pointers language-lawyer

假设我有一些结构定义如下:

struct foo { int a; };
struct bar { struct foo r; int b; };
struct baz { struct bar z; int c; };
Run Code Online (Sandbox Code Playgroud)

C标准是否保证以下代码严格符合要求?

struct baz x;
struct foo *p = (void *)&x;
assert(p == &x.z.r);
Run Code Online (Sandbox Code Playgroud)

这种结构的动机是提供一致的编程习惯用于转换为已知兼容的指针类型.


现在,这就是C所说的结构及其初始成员如何可转换:

在结构对象中,非位字段成员和位字段所在的单元具有按声明顺序增加的地址.指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然.结构对象中可能存在未命名的填充,但不是在其开头.
C.11§6.7.2.115

这就是void指针转换的内容:

指针void可以转换为指向任何对象类型的指针.指向任何对象类型的指针可以转换为指向void和返回的指针; 结果应该等于原始指针.
C.11§6.3.2.31

这就是关于在对象指针类型之间进行转换的说法:

指向对象类型的指针可以转换为指向不同对象类型的指针.如果对于引用的类型,结果指针未正确对齐(68),则行为未定义.否则,当再次转换回来时,结果将等于原始指针.

68)一般来说,''正确对齐''这个概念是传递性的:如果一个指向类型A的指针正确地对齐指向类型B的指针,而指针又指向一个指向类型C的指针,那么指向类型的指针对于指向类型C的指针,A正确对齐
.C.11§6.3.2.37

我从上面的理解是,通过转换将对象指针void *转换为不同类型的对象指针是完全正确的.但是,我得到的评论表明不然.

Ser*_*sta 4

你的例子是严格遵守的。

§6.7.2.1 ¶15 中的第二句(指向结构对象的指针,经过适当转换,指向初始成员......反之亦然。)保证以下等式:

(sruct bar *) &x == &(x.z)
(struct foo *) &(x.z) == &(x.z.r)
Run Code Online (Sandbox Code Playgroud)

由于您位于 的开头struct,因此不会发生填充,我对标准的理解是 a 的地址struct和它的第一个元素的地址是相同的。

所以struct foo *p = (void *) &x;是正确的struct foo *p = (struct foo *) &x;

在这种特殊情况下,根据 §6.7.2.1 ¶15 保证对齐是正确的。并且总是允许通过 a 传递void *,但这不是必需的,因为§6.3.2.3 ¶7 允许指向不同对象的指针之间的转换,前提是不存在对齐问题

应该注意的是,§6.2.3.2 ¶7 还说:当指向对象的指针转换为指向字符类型的指针时,结果指向对象的最低寻址字节,这意味着所有这些指针都指向事实上到 的最低寻址字节x.r.z.a。所以你也可以通过指针传递给,char因为我们还有:

(char *) &x == (char *) &(x.z) == (char *) &(x.z.r) == (char *) &(x.z.r.a)
Run Code Online (Sandbox Code Playgroud)