考虑以下示例程序:
#include <stdio.h>
struct base {
int a, b;
};
struct embedded {
struct base base;
int c, d;
};
struct pointed {
struct base* base;
int c, d;
};
static void base_print(struct base* x) {
printf("a: %d, b: %d\n", x->a, x->b);
}
static void tobase_embedded(void* object) {
base_print(object); // no cast needed, suitably converted into first member.
}
static void tobase_pointed(void* object) {
struct base* x = *(struct base**) object; // need this cast?
base_print(x);
}
int main(void) {
struct embedded em = {{4, 2}};
struct pointed pt = {&em.base};
tobase_embedded(&em);
tobase_pointed(&pt);
return 0;
}
编译:
$ gcc -std=c99 -O2 -Wall -Werror -pedantic -o main main.c
预期输出为:
$ ./main
a: 4, b: 2
a: 4, b: 2
C99标准说明了结构的第一个成员:
C99 6.7.2.1(13): 指向适当转换的结构对象的指针指向其初始成员...反之亦然。 在结构对象中可能有未命名的填充,但不在其开头。
在示例程序中,指向struct embedded
的指针将转换为指向struct base
(通过void*
)的指针,而无需显式转换。
如果相反第一个成员是指向struct pointed
的指针,那该怎么办?我不确定tobase_pointed
内的演员阵容。没有铸造垃圾被打印,但没有编译警告/错误。使用强制转换时,会打印base.a
和base.b
的正确值,但如果存在未定义的行为,那么这并不是很有意义。
转化为struct pointed
转换为第一个成员struct base*
是否正确?
答案 0 :(得分:2)
代码不只是强制转换,它还取消引用指向struct base的指针。这对于首先获得指向base的指针是必要的。
如果删除了函数tobase_pointed
,则代码中会发生这种情况:
struct pointed pt = {&em.base};
void* object = &pt; //pass to the function
struct base** bs = object; //the cast in the function
assert( bs == (struct base**)&pt ) ; //bs points to the struct pointed
assert( bs == &(pt.base) ) ; //bs also points to the initial member struct base* base
struct base* b = *bs ; //the dereference in the function
base_print(x);
bs
是适当转换为指向初始成员的指针。你的代码是正确的。
答案 1 :(得分:1)
这个演员是合理的,你需要它,因为你想将指针转换成指针指针。如果你没有演员,取消引用将是不正确的。
换句话说,您的base*
与pt
对象具有相同的地址。所以你可以通过指向pt的指针来访问它。但你必须取消引用它。