Полу-наследование в C: Как работает этот фрагмент? - PullRequest
5 голосов
/ 24 июля 2010

Один из способов взломать ограниченную форму полиморфизма в C - сделать что-то вроде этого:

typedef struct {
    int x;
} base;

typedef struct {
    base super;
    int y;
} derived;

Теперь вы можете ссылаться на производный экземпляр как на базовый экземпляр, в зависимости от того, как приведена переменная, например:

derived my_derived;
my_derived.y = 10;
my_derived.super.x = 20;
//will print 10
printf("%d", (&my_derived)->y);
//will print 20
printf("%d", ((base*)(&my_derived) )->x);

Итак, мой вопрос, как именно это работает? Это потому, что когда вы приводите его как base и ссылаетесь на переменную, вы ссылаетесь на член int 'x' как на смещение от начала структуры base? Это единственное, о чем я могу думать, любая помощь будет оценена.

Большое спасибо!

Ответы [ 3 ]

11 голосов
/ 24 июля 2010

В структуре могут быть безымянные байты заполнения между элементами данных или в конце структуры, но не в начале. Таким образом, адрес первого элемента данных объекта типа структуры гарантированно совпадает с адресом самого объекта типа структуры.

Итак, в вашем примере адрес my_derived совпадает с адресом my_derived.super.

2 голосов
/ 18 июля 2014

Структура - это память в байтовой области, в которой компилятор знает свою структуру, то есть, какие переменные вы объявляете внутри.

Например, вы можете объявить структуру:

struct st {
  int number;
};

struct st n;
n.number = 10;
printf("n=%i\n", n.number);

НоВы можете изменить поведение компилятора, например, объявить указатель на char над вашей структурой:

char *c = (char*)&n;
printf("char c=%c\n", c[0]);

Это допустимое объявление.Тогда вы можете в любой момент изменить структуру этой зоны памяти.Единственная важная вещь - это адрес памяти вашей объявленной структуры.

В вашем примере, когда вы объявляете производную структуру, программа резервирует область памяти для выделения производной структуры, но форма, которую компилятор видит эту область, можетможет быть изменено в любое время:

struct derived my_derived;
struct base *b = (struct base*)&my_derived;

b->x = 20;
my_derived.y = 10;
printf("x=%i y=%i\n", my_derived.base.x, my_derived.y);

В этом случае b и & my_derived совместно используют одну и ту же область памяти, вы изменяете только то, как компилятор "видит" эту область.

Использование "типа"«Пуннинг» - это основа имитации наследия oop в C, язык программирования не oop.

Я использую эту технику в своих проектах: oop4c

1 голос
/ 24 июля 2010

Это потому, что когда вы приводите его в качестве базы и ссылаетесь на переменную, вы ссылаетесь на член int 'x' как на смещение от начала структуры 'base'?

Да.Эту технику иногда называют «штампованием типа».

Это используется в стандартной библиотеке POSIX;например, в структуре sockaddr.Обычно вы объявляете его как sockaddr_storage, передаете его как sockaddr и манипулируете им как sockaddr_in или _in6 в зависимости от того, какой адрес на самом деле хранится в нем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...