C结构体初始化可以“递归”完成

5
我最近发现了一个C结构初始化的例子,这个例子是通过这个问题解释的。
我不理解的是似乎存在递归定义;这来自于MicroPython/objtype.c
typedef struct _mp_obj_type_t mp_obj_type_t;

const mp_obj_type_t mp_type_type = { // <-- SAME SYMBOL DEFINED HERE . . .
    { &mp_type_type }, // <-- IS USED HERE
    .name = MP_QSTR_type,
    .print = type_print,
    .make_new = type_make_new,
    .call = type_call,
    .unary_op = mp_generic_unary_op,
    .attr = type_attr,
};

.<some_field> 指定的字段我理解了(请参见第一句中的链接)。

但是"递归"初始化是什么意思呢?

MicroPython代码中还有其他使用这种语法的实例:

const mp_obj_type_t pyb_led_type = {
    { &mp_type_type }, <-- SAME SYMBOL AS ABOVE
    .name = MP_QSTR_LED,
    .print = led_obj_print,
    .make_new = led_obj_make_new,
    .locals_dict = (mp_obj_t)&led_locals_dict,
};

这更有意义:结构体pyb_led_type是使用结构体mp_type_type中设置的默认值进行初始化的,并且某些字段已从默认值更改。

但是const mp_obj_type_t mp_type_type是什么意思?

结构体mp_type_type默认为结构体mp_type_type的值......结构体mp_type_type......?

预处理输出与.c相同。

这里是结构体的一些字段。

struct _mp_obj_type_t {
    // A type is an object so must start with this entry, which points to mp_type_type.
    mp_obj_base_t base;

    // The name of this type.
    qstr name;

    // Corresponds to __repr__ and __str__ special methods.
    mp_print_fun_t print;

    ...
};
struct _mp_obj_base_t {
    const mp_obj_type_t *type MICROPY_OBJ_BASE_ALIGNMENT;
};
typedef struct _mp_obj_base_t mp_obj_base_t;

1
没有从其他值的默认设置。mp_obj_type_t中的一个字段是指针,并且它被初始化为指针值。 - melpomene
这与 void * p = &p; 相同。 - alk
struct _mp_obj_type_t的定义是什么?它似乎创建了一个包含指向自身的指针的结构体,用于某种用途。我很好奇这是如何使用的,为什么会有这种指向自身的指针。我无法想象出为什么这种自引用对象会有用,但这可能是因为我缺乏想象力。 - Richard Chambers
@RichardChambers 我更新了定义。现在一切都有意义了。 - Bob
看起来他们正在使用C语言进行一种面向对象、继承类型的构造。因此,他们从一个所有对象共享的基本对象开始,然后扩展这个基本对象以提供额外的功能。因此,第一个“struct”成员是一种虚拟方法表。 - Richard Chambers
显示剩余2条评论
3个回答

2

C语言中的自引用结构体

你引用的MicroPython代码只是创建了一个自引用结构体实例,在C语言中是完全合法的。考虑以下示例,它基本上是你的示例去除了一些不必要的部分:

#include "stdio.h"

// const base
struct A {
    const struct A* base;
};

// non-const base
struct B {
    const struct B* base;
};

const struct A a = { &a };

const struct B b = { &b };

int main() {
    printf("%p %p\n", (void*) &a, (void*)a.base);
    printf("%p %p\n", (void*) &b, (void*)b.base);
    return 0;
}

mp_obj_type_t在MicroPython的代码中的具体用途

MicroPython项目使用base指针来实现Python中的(多重)继承。这个base引用是指向另一个类型的指针,该类型是基础类型(类型层次结构中的“父级”),查看此结构的定义

struct _mp_obj_type_t {
    // A type is an object so must start with this entry, which points to mp_type_type.
    mp_obj_base_t base;
   // .. many more fields
}

您所提到的情况是关于mp_type_type常量变量似乎是所有类型的基础类型,因此存在自引用,但是当您查看从mp_type_type“继承”的类型,例如pyb_led_type时,它就更有意义了。
const mp_obj_type_t pyb_led_type = {
    { &mp_type_type },
    .name = MP_QSTR_LED,
    .print = led_obj_print,
    .make_new = led_obj_make_new,
    .locals_dict = (mp_obj_t)&led_locals_dict, };

0

在这个系统(MicroPython)中,对象从指向它们的类型的指针开始。MicroPython类型表示为mp_type_type类型的对象。mp_type_type本身就是一种类型,因此它的类型字段指向自身。(这个概念可能最好在Smalltalk文献中进行说明。参见:http://pharo.gforge.inria.fr/PBE1/PBE1ch14.html


-1
const mp_obj_type_t mp_type_type = { // <-- SAME SYMBOL DEFINED HERE . . .
    { &mp_type_type }, // <-- IS USED HERE
    .name = MP_QSTR_type,

...

};

The fields specified by . I understand (see link in first sentence).

But about the "recursive" initialization?

没有任何东西可以合理地被描述为递归初始化。这将是使用该对象或其成员的来初始化对象或其成员,C确实禁止这样做,但没有证据表明有这样的事情。

您的示例显示正在使用该对象的地址(这就是&运算符计算的内容)来初始化对象的成员。对象的地址与该对象的值无关,因此可以在初始化对象之前安全地计算它。从一般意义上讲,这种做法甚至并不罕见,尽管特别是使用指向该对象的指针来初始化对象的一部分是不寻常的。


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