Инициализация структуры C выполняется " рекурсивно"
Я недавно столкнулся с примером инициализации 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>
, я понимаю (см. ссылку в первом предложении).
А как же" рекурсивная " инициализация?
Есть и другие экземпляры в Микропитонном коде , которые используют это синтаксис:
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
по умолчанию имеет значения . . . struct 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;
3 ответа:
Самореферентные структуры в C
Код Микропифона, который вы цитируете, просто создает самореферентный экземпляр структуры, что совершенно нормально в 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; }
Специфическое использование в создании экземпляров
Проект MicroPython использует указательmp_obj_type_t
структур в коде Микропифона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
переменная const, кажется, базовый тип всех типов, таким образом, само-ссылка, но это имеет гораздо больше смысла, когда вы смотрите на типы, которые "наследуют" от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, };
Объекты в этой системе (Микропитон) начинаются с указателя на их тип. Типы микропитонов представлены в виде объектов типа mp_type_type. Mp_type_type - это сам тип, поэтому его поле типа указывает на него самого. (Концепция, пожалуй, лучше всего иллюстрируется в литературе по Smalltalk. cf: http://pharo.gforge.inria.fr/PBE1/PBE1ch14.html возможно.)
const mp_obj_type_t mp_type_type = { // <-- SAME SYMBOL DEFINED HERE . . . { &mp_type_type }, // <-- IS USED HERE .name = MP_QSTR_type,
...
Там нет ничего, что можно было бы обоснованно охарактеризовать как рекурсивную инициализацию. Это было бы инициализацией объекта или одного из его членов с значением этого объекта, что C действительно запрещает, но такая вещь не является очевидной.};
Поля, указанные пользователем . Я понимаю (см. ссылку в первом разделе предложение).
А как же "рекурсивная" инициализация?В вашем примере показан член объект инициализируется адресом этого объекта (именно это вычисляет оператор
&
). Адрес объекта никоим образом не зависит от его значения, и его можно безопасно вычислить до инициализации объекта. Такая практика даже не является чем-то необычным в общем смысле, хотя это необычно специально инициализировать часть объекта указателем на этот объект.