2010-07-23 33 views
5

Một cách hack hình thức hạn chế về tính đa hình trong C là để làm một cái gì đó như thế này:Nửa kế thừa trong C: Đoạn mã này hoạt động như thế nào?

typedef struct { 
    int x; 
} base; 

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

Bây giờ bạn có thể tham khảo một trường hợp có nguồn gốc như một trường hợp cơ sở, tùy thuộc vào cách biến là diễn viên, ví dụ:

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); 

Vì vậy, câu hỏi của tôi là, tính năng này hoạt động như thế nào? Có phải vì khi bạn cast nó như là cơ sở và tham chiếu một biến, bạn đang tham chiếu đến thành viên int 'x' như là offset từ đầu của cấu trúc 'base'? Đây là điều duy nhất tôi có thể nghĩ đến, bất kỳ trợ giúp nào cũng sẽ được đánh giá cao.

Cảm ơn rất nhiều!

Trả lời

11

Trong cấu trúc, có thể có byte đệm không đặt tên giữa các phần tử dữ liệu hoặc ở cuối cấu trúc, nhưng không phải ở đầu. Vì vậy, địa chỉ của phần tử dữ liệu đầu tiên của một đối tượng kiểu struct được bảo đảm giống với địa chỉ của đối tượng kiểu struct.

Vì vậy, trong ví dụ của bạn, địa chỉ của my_derived giống với địa chỉ my_derived.super.

+2

+1 Điều này thực sự được đảm bảo bởi thông số C. – bta

1

Có phải vì khi bạn đặt nó làm cơ sở và tham chiếu biến, bạn đang tham chiếu thành viên int 'x' làm giá trị bù trừ từ đầu của cấu trúc 'cơ sở'?

Có. Kỹ thuật này đôi khi được gọi là "loại punning".

Điều này được sử dụng trong thư viện chuẩn POSIX; ví dụ, trong struct sockaddr. Thông thường bạn khai báo một sockaddr_storage, chuyển nó thành một sockaddr, và thao tác nó như một sockaddr_in hoặc _in6 tùy thuộc vào loại địa chỉ nào thực sự được lưu trữ bên trong nó.

2

Cấu trúc là bộ nhớ vùng byte mà trình biên dịch biết cấu trúc của nó, nghĩa là, biến nào bạn khai báo bên trong.

Ví dụ, bạn có thể khai báo một struct:

struct st { 
    int number; 
}; 

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

Nhưng bạn có thể thay đổi hành vi biên dịch, ví dụ khai báo một con trỏ đến char trên struct của bạn:

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

Đây là một tuyên bố pháp lý . Sau đó, bạn có thể thay đổi bất cứ lúc nào cấu trúc của vùng bộ nhớ đó. Điều quan trọng duy nhất là địa chỉ bộ nhớ của cấu trúc đã khai báo của bạn.

Trong ví dụ của bạn, khi khai báo cấu trúc bắt nguồn dự trữ chương trình một khu vực bộ nhớ để phân bổ các stucture có nguồn gốc, nhưng hình thức mà trình biên dịch thấy khu vực này có thể thay đổi bất cứ lúc nào:

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); 

Trong trường hợp này b và & my_derived chia sẻ cùng một vùng nhớ, bạn chỉ thay đổi cách trình biên dịch "xem" vùng này.

Việc sử dụng "loại punning" là cơ sở của mô phỏng di sản oop trong C, một ngôn ngữ lập trình phi oop.

Tôi sử dụng kỹ thuật này trong các dự án của mình: oop4c

Các vấn đề liên quan