2016-01-22 18 views
8

Hãy xem xét các chương trình ví dụ sau:Chuyển đổi một con trỏ đến một cấu trúc thành viên đầu tiên của mình

#include <stdio.h> 

struct base { 
    int a, b; 
}; 

struct embedded { 
    struct base base; 
    int c, d; 
}; 

struct pointed { 
    struct base* base; 
    int c, d; 
}; 

static void base_print(struct base* x) { 
    printf("a: %d, b: %d\n", x->a, x->b); 
} 

static void tobase_embedded(void* object) { 
    base_print(object); // no cast needed, suitably converted into first member. 
} 

static void tobase_pointed(void* object) { 
    struct base* x = *(struct base**) object; // need this cast? 
    base_print(x); 
} 

int main(void) { 
    struct embedded em = {{4, 2}}; 
    struct pointed pt = {&em.base}; 
    tobase_embedded(&em); 
    tobase_pointed(&pt); 
    return 0; 
} 

Biên soạn với:

$ gcc -std=c99 -O2 -Wall -Werror -pedantic -o main main.c 

Sản lượng dự kiến ​​là:

$ ./main 
a: 4, b: 2 
a: 4, b: 2 

Các C99 tiêu chuẩn nói về thành viên đầu tiên của cấu trúc:

C99 6.7.2.1 (13): Một con trỏ tới đối tượng cấu trúc, được chuyển đổi phù hợp, trỏ đến thành viên ban đầu của nó ... và ngược lại. Có thể có đệm không tên trong phạm vi đối tượng cấu trúc, nhưng không phải lúc bắt đầu.

Trong chương trình ví dụ như một con trỏ đến struct embedded được chuyển thành một con trỏ đến struct base (thông qua void*) mà không cần chuyển kiểu tường minh.

Điều gì sẽ xảy ra nếu thay vào đó thành viên đầu tiên là con trỏ đến cơ sở như trong struct pointed? Tôi không chắc về dàn diễn viên trong phạm vi tobase_pointed. Nếu không có thùng rác được in, nhưng không có cảnh báo/lỗi biên dịch. Với việc đúc các giá trị chính xác cho base.abase.b được in, nhưng điều đó không thực sự có ý nghĩa nhiều nếu có hành vi không xác định.

Dàn diễn viên có chuyển đổi struct pointed thành thành viên đầu tiên struct base* chính xác không?

+0

Thành viên đầu tiên của 'struct pointed' là một con trỏ tới' struct base'. Điều này sẽ yêu cầu dereferencing 'void * object'. Nhưng bạn không thể dereference 'void *' mà không nói trình biên dịch làm thế nào để dereference con trỏ, do đó cần phải cast. – alvits

Trả lời

2

Mã không chỉ hoạt động, nó cũng dereferences con trỏ đến con trỏ đến cơ sở struct. Điều này là cần thiết để có được con trỏ đến cơ sở ở nơi đầu tiên.

Đây là những gì xảy ra trong mã của bạn, nếu chức năng tobase_pointed đã được gỡ bỏ:

struct pointed pt = {&em.base}; 
void* object = &pt;     //pass to the function 
struct base** bs = object;   //the cast in the function 
assert(bs == (struct base**)&pt) ; //bs points to the struct pointed 
assert(bs == &(pt.base)) ;   //bs also points to the initial member struct base* base 
struct base* b = *bs ;    //the dereference in the function 
base_print(x); 

bs là con trỏ đã được chuyển đổi phù hợp để trỏ đến các thành viên ban đầu. Mã của bạn là chính xác.

+1

Nhìn thấy nó từng bước làm cho nó tinh thể rõ ràng, cảm ơn. – Adam

1

Dàn diễn viên này được hợp lý và bạn cần nó vì bạn muốn chuyển đổi con trỏ thành con trỏ thành con trỏ. Nếu bạn không cast, dereference sẽ không chính xác.

Nói cách khác, số base* của bạn có cùng địa chỉ với đối tượng pt. Vì vậy, bạn có thể truy cập nó thông qua một con trỏ đến pt. Nhưng bạn phải dereference nó.

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