将指向结构体的指针转换为其第一个成员

8

Consider the following example program:

#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): 指向结构体对象的指针被适当转换后,指向其初始成员...反之亦然。在结构体对象内可能有未命名的填充位,但不能在开头处。
在示例程序中,通过void*将指向struct embedded的指针转换为指向struct base的指针,无需显式转换。
如果第一个成员是指向基类的指针,例如struct pointed中的情况呢?我不确定tobase_pointed中的显式转换是否正确。没有进行转换时会输出垃圾字符,但没有编译警告/错误。进行转换时,将输出base.a和base.b的正确值,但如果存在未定义行为,则意义不大。
将struct pointed转换为其第一个成员struct base*的转换是否正确?

struct pointed 的第一个成员是指向 struct base 的指针。这将需要对 void *object 进行解引用。但是,您不能在不告诉编译器如何解引用指针的情况下对 void * 进行解引用,因此需要进行强制类型转换。 - alvits
2个回答

3

代码不仅进行类型转换,还会对指向结构体基类的指针进行解引用。这是为了首先获取指向基类的指针而必需的。

如果函数 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
看到它逐步展示,让人清晰明了,谢谢。 - Adam

1
这个转换是有必要的,因为你想将一个指针转换成指向指针的指针。如果不进行转换,解引用将会出现错误。
换句话说,你的base*pt对象具有相同的地址。因此,你可以通过指向pt的指针来访问它。但你必须对它进行解引用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接