tự định nghĩa cấu trúc tham chiếu?


134

Tôi đã không viết C rất lâu và vì vậy tôi không chắc mình nên làm thế nào để thực hiện những điều đệ quy này ... Tôi muốn mỗi ô chứa một ô khác, nhưng tôi gặp lỗi dòng "trường 'con' có kiểu không đầy đủ". Có chuyện gì vậy?

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

9
PS Trên thực tế, nó đã gõ "struct Cell" thành "Cell" (đó là một mô hình phổ biến)
David Z

có lẽ anh ta đang sử dụng trình biên dịch C ++. anh ấy cũng nên sử dụng _Bool nếu nó thực sự C.
nabiy

Anh ta nên sử dụng int nếu nó thực sự C :-)
paxdiablo

2
Tại sao? C99 có bool - bạn chỉ cần bao gồm <stdbool.h>
Jonathan Leffler

Câu trả lời:


184

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

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

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

7
@ cs01 Không, Cellchưa có trong phạm vi.
dòng chảy

1
sẽ có ý nghĩa. Python cho phép nó và thậm chí cho phép tuần tự hóa một đối tượng như vậy. Tại sao không phải là C ++?
noɥʇʎԀʎzɐɹƆ

1
Tôi nhận được cảnh báo khi tôi cố gắng gán Cell*cho cell->child.
Tomáš Zato - Phục hồi Monica

3
@ noɥʇʎԀʎzɐɹƆ Vì Python trừu tượng hóa các con trỏ để bạn không chú ý đến chúng. Vì structs trong C về cơ bản chỉ lưu trữ tất cả các giá trị của chúng cạnh nhau, nên thực sự không thể lưu trữ một cấu trúc trong chính nó (vì cấu trúc đó sẽ phải chứa một cấu trúc khác, v.v., dẫn đến cấu trúc bộ nhớ có kích thước vô hạn) .
jazzpi

1
Đối với một lời giải thích về việc sử dụng struct Cell, xem câu trả lời này .
wizzwizz4

26

Trong C, bạn không thể tham chiếu typedef mà bạn đang tạo với 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:

#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ể phức tạp hơn nhiều so với tiêu chuẩn này, nhưng bạn có thể nghĩ nó là trình biên dịch biết về struct Celldòng đầu tiên typedefnhưng không biết tCellcho đến dòng cuối cùng :-) Đó là cách tôi nhớ quy tắc đó.


những gì về c ++, bạn có thể vui lòng liên kết các câu trả lời liên quan đến c ++
rimalonfire

@rimiro, câu hỏi là một C. Nếu bạn muốn câu trả lời cho một biến thể C ++, bạn nên hỏi nó như một câu hỏi.
paxdiablo

16

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


Từ quan điểm thực tế, một ví dụ của 'struct Cell' thực sự sẽ lớn đến mức nào?
Marsh Ray

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

13

Có một cách để giải quyết vấn đề này:

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

struct Cell;
typedef struct Cell Cell;

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


9
tại sao bạn struct Cell;lại viết
MAKZ

@MAKZ vì typedef chưa được trình biên dịch thực thi tại thời điểm nó biên dịch định nghĩa struct Cell.
Tyler Crompton

2
@TylerCrompton nếu khối mã trên được đưa vào một tệp nguồn C duy nhất, thì typedef đã được "thực hiện bởi trình biên dịch", làm cho struct Cell;dự phòng thêm . Tuy nhiên, nếu vì một 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 trước khi bạn xác định Cellcấu trúc với bốn dòng đầu tiên, thì phần bổ sung struct Cell;là cần thiết.
yyny

Điều này thậm chí không biên dịch theo tiêu chuẩn C99.
Tomáš Zato - Phục hồi Monica

2
@YoYoYonnY Không, bạn vẫn có thể viết typedef struct Cell Cell;và nó sẽ tạo Cellbí danh cho struct Cell. Nó không quan trọng cho dù trình biên dịch đã nhìn thấy struct Cell { .... }trước đó.
melpomene

8

Tôi biết bài đăng này đã cũ, tuy nhiên, để có được hiệu quả mà bạn đang tìm kiếm, bạn có thể muốn thử các cách 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 được đề cập trong đoạn mã ở trên, bạn PHẢI khai báo cấu trúc ô con của bạn dưới dạng con trỏ. Nếu không, thì bạn sẽ gặp lỗi "trường 'con' có kiểu không hoàn chỉnh". Lý do là "struct cell" phải được xác định để trình biên dịch biết được phân bổ bao nhiêu dung lượng khi nó được sử dụng.

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


3

Hãy đi qua định nghĩa cơ bản của typedef. typedef sử dụng để xác định bí danh cho kiểu dữ liệu hiện có hoặc do người dùng xác định hoặc sẵn có.

typedef <data_type> <alias>;

ví dụ

typedef int scores;

scores team1 = 99;

Nhầm lẫn ở đây là với cấu trúc tự tham chiếu, do một thành viên của cùng loại dữ liệu 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 làm tăng thêm một số dòng và từ thông thường mà chúng tôi không muốn làm (chúng tôi rất lười bạn biết đấy;)). Vì vậy, thích Xem 2.


Giải thích về typedefcú pháp của bạn là không chính xác (xem xét ví dụ typedef int (*foo)(void);). Ví dụ Chế độ xem 1 và Chế độ xem 2 của bạn không hoạt động: Chúng tạo ra struct Cellmột loại không hoàn chỉnh, do đó bạn thực sự không thể sử dụng childmã của mình.
melpomene

3

Một phương pháp tiện lợi khác là pre-typedef cấu trúc với, thẻ cấu trúc là:

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

1

Một cấu trúc có chứa một 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 danh sách liên kết. Mỗi nút cần một tham chiếu đến nút tiếp theo trong chuỗi.

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

1

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

Điều rất quan trọng cần lưu ý là các cấu trúc là các loại 'giá trị' tức là chúng chứa giá trị thực, vì vậy khi bạn khai báo một cấu trúc, trình biên dịch phải quyết định phân bổ bao nhiêu bộ nhớ cho một thể hiện của nó, để nó đi qua tất cả các thành viên của nó và thêm vào bộ nhớ của họ để tìm ra toàn bộ bộ nhớ của struct, 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 cấu trúc bộ nhớ A cần bao nhiêu để bạn quyết định bao nhiêu bộ nhớ cấu trúc A mất!).

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

HTH

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.