2009-02-26 39 views
97

Tôi đã không viết C cho rất lâu, và vì vậy tôi không chắc chắn về cách tôi nên đi về làm những loại đệ quy những điều ... Tôi muốn mỗi tế bào chứa một tế bào, nhưng tôi nhận được một lỗi dọc theo dòng của "trường" con "có loại không đầy đủ". Chuyện gì thế?định nghĩa struct tự tham chiếu?

typedef struct Cell { 
    int isParent; 
    Cell child; 
} Cell; 

PS (Ziggy cũng đang bối rối rõ ràng bởi typedef: ông đã typedefed Cell-Cell và tự hỏi tại sao?)

+9

PS Trên thực tế nó Typedefs "struct di động" thành "di động" (đó là một mô hình phổ biến) –

+1

Và nó là tự động trong C++. –

+0

Vui lòng gắn lại với C++ nếu đó là ý định của bạn (tôi cho rằng đó là vì bạn đã chấp nhận câu trả lời không hoạt động trong C và bạn đã sử dụng bool (mặc dù cũng có thể được đánh máy trong C)). – paxdiablo

Trả lời

147

Rõ ràng một tế bào không thể chứa tế bào khác vì nó sẽ trở thành một đệ quy không bao giờ có điểm dừng.

Tuy nhiên, ô có thể chứa con trỏ đến ô khác.

typedef struct Cell { 
    bool isParent; 
    struct Cell* child; 
} Cell; 
+4

@ cs01 Không, 'Ô' chưa nằm trong phạm vi. – fredoverflow

+1

Nó * sẽ * có ý nghĩa. Python cho phép nó và thậm chí cho phép serialization của một đối tượng như vậy. Tại sao không C++? –

+0

Tôi nhận được cảnh báo khi tôi cố gắng gán 'Cell *' cho 'cell-> child'. –

19

Trong C (trái ngược với C++ nếu có thể, tôi chưa chọn), bạn không thể tham chiếu typedef mà bạn đang tạo bằng chính cấu trúc đó. Bạn phải sử dụng tên cấu trúc, như trong chương trình thử nghiệm sau đây:

#include <stdio.h> 
#include <stdlib.h> 

typedef struct Cell { 
    int cellSeq; 
    struct Cell* next; /* tCell *next will not work here */ 
} tCell; 

int main(void) { 
    int i; 
    tCell *curr; 
    tCell *first; 
    tCell *last; 

    /* Construct linked list, 100 down to 80. */ 

    first = malloc (sizeof (tCell)); 
    last = first; 
    first->cellSeq = 100; 
    first->next = NULL; 
    for (i = 0; i < 20; i++) { 
     curr = malloc (sizeof (tCell)); 
     curr->cellSeq = last->cellSeq - 1; 
     curr->next = NULL; 
     last->next = curr; 
     last = curr; 
    } 

    /* Walk the list, printing sequence numbers. */ 

    curr = first; 
    while (curr != NULL) { 
     printf ("Sequence = %d\n", curr->cellSeq); 
     curr = curr->next; 
    } 

    return 0; 
} 

Mặc dù nó có thể là rất nhiều phức tạp hơn này trong tiêu chuẩn, bạn có thể nghĩ về nó như trình biên dịch biết về struct Cell trên dòng đầu tiên của số typedef nhưng không biết về tCell cho đến dòng cuối cùng :-) Đó là cách tôi nhớ quy tắc đó.

11

Có loại một khoảng cách này:

struct Cell { 
    bool isParent; 
    struct Cell* child; 
}; 

struct Cell; 
typedef struct Cell Cell; 

Nếu bạn khai báo nó như thế này, nó đúng cho trình biên dịch rằng struct di động và đồng bằng-ol' cell đều giống nhau. Vì vậy, bạn có thể sử dụng Cell giống như bình thường. Tuy nhiên vẫn phải sử dụng struct Cell bên trong bản khai báo ban đầu.

+8

tại sao bạn lại viết 'struct Cell;' một lần nữa? – MAKZ

+0

@MAKZ vì typedef chưa được trình biên dịch thực hiện tại thời điểm biên dịch định nghĩa 'struct Cell'. –

+1

@TylerCrompton nếu khối mã ở trên được đặt vào một tệp nguồn C duy nhất, thì typedef _has_ được "thực thi bởi trình biên dịch", tạo thêm 'struct Cell;' dư thừa.Tuy nhiên, nếu vì lý do nào đó, bạn đặt hai dòng cuối cùng vào một tệp tiêu đề mà bạn đưa vào _before_, bạn xác định cấu trúc 'Cell' với bốn dòng đầu tiên, sau đó * thêm' struct Cell; 'là nececairy. – YoYoYonnY

11

Từ quan điểm lý thuyết, Ngôn ngữ chỉ có thể hỗ trợ cấu trúc tự tham chiếu chứ không phải cấu trúc tự bao gồm.

+0

Từ quan điểm thực tế, làm thế nào lớn như vậy một ví dụ của 'struct Cell' thực sự được? –

+18

Trên hầu hết các máy, bốn byte lớn hơn chính nó. – TonyK

2

Cấu trúc có chứa tham chiếu đến chính nó. Một sự xuất hiện phổ biến của điều này trong một cấu trúc mô tả một nút cho một danh sách liên kết. Mỗi nút cần tham chiếu đến nút tiếp theo trong chuỗi.

struct node 
{ 
     int data; 
     struct node *next; // <-self reference 
}; 
6

Tôi biết bài này là cũ, tuy nhiên, để có được hiệu quả bạn đang tìm kiếm, bạn có thể muốn thử như sau:

#define TAKE_ADVANTAGE 

/* Forward declaration of "struct Cell" as type Cell. */ 
typedef struct Cell Cell; 

#ifdef TAKE_ADVANTAGE 
/* 
    Define Cell structure taking advantage of forward declaration. 
*/ 
struct Cell 
{ 
    int isParent; 
    Cell *child; 
}; 

#else 

/* 
    Or...you could define it as other posters have mentioned without taking 
    advantage of the forward declaration. 
*/ 
struct Cell 
{ 
    int isParent; 
    struct Cell *child; 
}; 

#endif 

/* 
    Some code here... 
*/ 

/* Use the Cell type. */ 
Cell newCell; 

Trong một trong hai trường hợp nêu tại các mã đoạn trên, bạn PHẢI khai báo cấu trúc ô con của bạn như một con trỏ. Nếu bạn không, sau đó bạn sẽ nhận được lỗi "field" child "có kiểu không đầy đủ". Lý do là "struct Cell" phải được định nghĩa để trình biên dịch biết được bao nhiêu không gian để phân bổ khi nó được sử dụng.

Nếu bạn cố gắng sử dụng "struct Cell" bên trong định nghĩa "struct Cell", thì trình biên dịch không thể biết bao nhiêu không gian "struct Cell" là nghĩa vụ phải thực hiện. Tuy nhiên, trình biên dịch đã biết bao nhiêu không gian con trỏ mất, và (với khai báo chuyển tiếp) nó biết rằng "Cell" là một loại "struct Cell" (mặc dù nó chưa biết lớn như thế nào "struct Cell" là). Vì vậy, trình biên dịch có thể định nghĩa một "Cell *" trong cấu trúc đang được xác định.

3

Cho phép đi qua định nghĩa cơ bản về typedef. typedef sử dụng để định nghĩa một bí danh cho một kiểu dữ liệu hiện có, hoặc nó được người dùng định nghĩa hoặc inbuilt.

typedef <data_type> <alias>; 

ví dụ

typedef int scores; 

scores team1 = 99; 

Lẫn lộn ở đây là với cấu trúc tự tham chiếu, do một thành viên của kiểu dữ liệu tương tự mà không được xác định trước đó. Vì vậy, theo cách tiêu chuẩn, bạn có thể viết mã của mình dưới dạng: -

//View 1 
typedef struct{ bool isParent; struct Cell* child;} Cell; 

//View 2 
typedef struct{ 
    bool isParent; 
    struct Cell* child; 
} Cell; 

//Other Available ways, define stucture and create typedef 
struct Cell { 
    bool isParent; 
    struct Cell* child; 
}; 

typedef struct Cell Cell; 

Nhưng tùy chọn cuối cùng sẽ tăng thêm một số dòng và từ thường không muốn (chúng tôi rất lười);). Vì vậy, thích Xem 2.

+0

Giải thích của bạn về cú pháp 'typedef' là không chính xác (xem ví dụ:' typedef int (* foo) (void); '). Ví dụ View 1 và View 2 của bạn không hoạt động: Chúng làm cho 'struct Cell' trở thành một kiểu không hoàn chỉnh, vì vậy bạn không thể sử dụng' child' trong mã của bạn. – melpomene

2

Một phương pháp thuận tiện là để cơ cấu với, thẻ cấu trúc như-typedef trước:

//declare new type 'Node', as same as struct tag 
typedef struct Node Node; 
//struct with structure tag 'Node' 
struct Node 
{ 
int data; 
//pointer to structure with custom type as same as struct tag 
Node *nextNode; 
}; 
//another pointer of custom type 'Node', same as struct tag 
Node *node; 
0

Tất cả các câu trả lời trước là rất lớn, tôi chỉ nghĩ để đưa ra một cái nhìn sâu sắc về lý do tại sao một cấu trúc có thể không chứa một thể hiện của kiểu riêng của nó (không phải là một tham chiếu).

Điều quan trọng cần lưu ý là cấu trúc là các kiểu 'giá trị' tức là chúng chứa giá trị thực, vì vậy khi bạn khai báo cấu trúc, trình biên dịch phải quyết định lượng bộ nhớ phân bổ cho một thể hiện của nó, các thành viên của nó và thêm bộ nhớ của họ để tìm ra tất cả bộ nhớ của cấu trúc, nhưng nếu trình biên dịch tìm thấy một thể hiện của cùng một cấu trúc bên trong thì đây là một nghịch lý (tức là để biết bộ nhớ cấu trúc A mất bao nhiêu quyết định bao nhiêu bộ nhớ cấu trúc A mất!).

Nhưng kiểu tham chiếu khác nhau, nếu cấu trúc 'A' chứa 'tham chiếu' đối với một thể hiện của loại riêng của nó, mặc dù chúng tôi chưa biết số lượng bộ nhớ được cấp cho nó, chúng ta biết bộ nhớ là bao nhiêu được cấp phát cho một địa chỉ bộ nhớ (tức là tham chiếu).

HTH

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