2010-07-23 3 views
5

Один из способов взломать ограниченную форму полиморфизма в C, чтобы сделать что-то вроде этого:Полунаследование в 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); 

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

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

ответ

11

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

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

+2

+1 Это фактически гарантированную C спецификации. – bta

1

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

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

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

2

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

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

struct st { 
    int number; 
}; 

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

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

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

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

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

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 используют одну и ту же область памяти, вы меняете только то, как компилятор «видит» эту область.

Использование «типа punning» является базой моделирования наследия oop в C, не связанным с программированием langugage.

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

Смежные вопросы