definisi struct referensial diri?

134

Saya sudah lama tidak menulis C, jadi saya tidak yakin bagaimana saya harus melakukan hal-hal rekursif semacam ini ... Saya ingin setiap sel mengandung sel lain, tetapi saya mendapatkan kesalahan di sepanjang baris "bidang 'anak' memiliki tipe tidak lengkap". Ada apa?

typedef struct Cell {
  int isParent;
  Cell child;
} Cell;
Ziggy
sumber
9
PS Sebenarnya itu typedef "struct Sel" untuk "Sel" (itu adalah pola umum)
David Z
dia mungkin menggunakan kompiler C ++. ia juga harus menggunakan _Bool jika itu benar-benar C.
nabiy
Dia harus menggunakan int jika itu benar-benar C :-)
paxdiablo
2
Mengapa? C99 memiliki bool - Anda hanya perlu memasukkan <stdbool.h>
Jonathan Leffler
1
kemungkinan duplikat dari pointer C: ke struct dalam definisi struct
Jonathan Leffler

Jawaban:

184

Jelas suatu sel tidak dapat mengandung sel lain karena ia menjadi rekursi yang tidak pernah berakhir.

Namun sebuah sel BISA berisi pointer ke sel lain.

typedef struct Cell {
  bool isParent;
  struct Cell* child;
} Cell;
Andrew Grant
sumber
7
@ cs01 Tidak, Cellbelum dalam cakupan.
fredoverflow
1
Ini akan masuk akal. Python memungkinkannya dan bahkan memungkinkan serialisasi objek semacam itu. Kenapa tidak C ++?
noɥʇʎԀʎzɐɹƆ
1
Saya mendapatkan peringatan ketika saya mencoba untuk menetapkan Cell*ke cell->child.
Tomáš Zato - Reinstate Monica
3
@ noɥʇʎԀʎzɐɹƆ Karena Python mengabstraksikan pointer sehingga Anda tidak melihatnya. Karena structs di C pada dasarnya hanya menyimpan semua nilai mereka di samping satu sama lain, tidak mungkin untuk benar-benar menyimpan struct itu sendiri (karena struct itu harus mengandung yang lain dan seterusnya, yang mengarah ke struktur memori ukuran tak terbatas) .
jazzpi
1
Untuk penjelasan tentang penggunaan struct Cell, lihat jawaban ini .
wizzwizz4
26

Di C, Anda tidak bisa mereferensikan typedef yang Anda buat dengan struktur itu sendiri. Anda harus menggunakan nama struktur, seperti dalam program pengujian berikut:

#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;
}

Meskipun mungkin jauh lebih rumit daripada ini dalam standar, Anda dapat menganggapnya sebagai kompiler yang mengetahui tentang struct Cellbaris pertama typedeftetapi tidak tahu tentang tCellsampai baris terakhir :-) Begitulah cara saya mengingat aturan itu.

paxdiablo
sumber
bagaimana dengan c ++ dapatkah Anda menautkan jawaban terkait c ++
rimalonfire
@rimiro, pertanyaannya adalah pertanyaan C. Jika Anda menginginkan jawaban untuk varian C ++, Anda harus melakukannya menanyakannya sebagai pertanyaan.
paxdiablo
16

Dari sudut pandang teoretis, Bahasa hanya dapat mendukung struktur referensi-diri bukan struktur inklusif.

Sundar
sumber
Dari sudut pandang praktis, seberapa besar contoh 'struct Cell' sebenarnya?
Marsh Ray
26
Pada kebanyakan mesin, empat byte lebih besar dari dirinya sendiri.
TonyK
13

Ada beberapa cara untuk mengatasi hal ini:

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

struct Cell;
typedef struct Cell Cell;

Jika Anda mendeklarasikannya seperti ini, ia dengan benar mengatakan kepada kompiler bahwa struct Cell dan plain-ol'-cell adalah sama. Jadi Anda bisa menggunakan Cell seperti biasa. Masih harus menggunakan struct Cell di dalam deklarasi awal itu sendiri.

Benjamin Horstman
sumber
9
kenapa kamu menulis struct Cell;lagi?
MAKZ
@MAKZ karena typedef belum dieksekusi oleh kompiler pada saat kompilasi definisi struct Cell.
Tyler Crompton
2
@TylerCrompton jika blok kode di atas dimasukkan ke dalam file sumber C tunggal, maka typedef telah "dieksekusi oleh kompiler", membuat ekstra struct Cell;berlebihan. Namun, jika karena alasan tertentu Anda memasukkan dua baris terakhir ke dalam file header yang Anda sertakan sebelum Anda mendefinisikan Cellstruct dengan empat baris pertama, maka tambahannya struct Cell;adalah nececairy.
yyny
Ini bahkan tidak dikompilasi di bawah standar C99.
Tomáš Zato - Reinstate Monica
2
@YoYoYonnY Tidak, Anda masih bisa menulis typedef struct Cell Cell;dan itu akan membuat Cellalias untuk struct Cell. Tidak masalah apakah kompiler telah melihat struct Cell { .... }sebelumnya.
melpomene
8

Saya tahu posting ini sudah tua, namun, untuk mendapatkan efek yang Anda cari, Anda mungkin ingin mencoba yang berikut:

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

Dalam salah satu dari dua kasus yang disebutkan dalam fragmen kode di atas, Anda HARUS mendeklarasikan struktur sel anak Anda sebagai pointer. Jika tidak, maka Anda akan mendapatkan kesalahan "bidang 'anak' memiliki tipe tidak lengkap". Alasannya adalah bahwa "struct Cell" harus didefinisikan agar kompiler mengetahui berapa banyak ruang yang dialokasikan ketika digunakan.

Jika Anda mencoba menggunakan "struct Cell" di dalam definisi "struct Cell", maka kompiler belum dapat mengetahui berapa banyak ruang yang harus diambil oleh "struct Cell". Namun, kompiler sudah tahu berapa banyak ruang yang dibutuhkan pointer, dan (dengan deklarasi maju) ia tahu bahwa "Sel" adalah jenis "sel struct" (walaupun belum tahu seberapa besar "sel Cell" adalah ). Jadi, kompiler dapat mendefinisikan "Sel *" di dalam struct yang sedang didefinisikan.

Shawn
sumber
3

Mari kita pergi melalui definisi dasar typedef. typedef digunakan untuk mendefinisikan alias ke tipe data yang ada baik itu yang ditentukan pengguna atau bawaan.

typedef <data_type> <alias>;

sebagai contoh

typedef int scores;

scores team1 = 99;

Kebingungan di sini adalah dengan struktur referensial diri, karena anggota dengan tipe data yang sama yang tidak didefinisikan sebelumnya. Jadi Dengan cara standar Anda dapat menulis kode Anda sebagai: -

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

Tetapi opsi terakhir menambah beberapa baris dan kata-kata tambahan dengan biasanya kita tidak ingin melakukan (kita sangat malas lho;)). Jadi lebih suka View 2.

vineetv2821993
sumber
Penjelasan Anda tentang typedefsintaksis salah (pertimbangkan misalnya typedef int (*foo)(void);). Contoh Lihat 1 dan 2 Lihat Anda tidak berfungsi: Mereka membuat struct Celljenis yang tidak lengkap, sehingga Anda tidak dapat benar-benar menggunakannya childdalam kode Anda.
melpomene
3

Metode lain yang mudah adalah dengan pra-mengetikkan struktur dengan, tag struktur sebagai:

//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;
Keynes
sumber
1

Struktur yang berisi referensi ke dirinya sendiri. Kejadian umum ini dalam struktur yang menggambarkan node untuk daftar tautan. Setiap node membutuhkan referensi ke node berikutnya dalam rantai.

struct node
{
       int data;
       struct node *next; // <-self reference
};
DARSHINI DESAI
sumber
1

Semua jawaban sebelumnya bagus, saya hanya berpikir untuk memberikan wawasan tentang mengapa suatu struktur tidak dapat memuat turunan dari jenisnya sendiri (bukan referensi).

itu sangat penting untuk dicatat bahwa struktur adalah tipe 'nilai' yaitu mengandung nilai aktual, jadi ketika Anda mendeklarasikan struktur, kompiler harus memutuskan berapa banyak memori yang dialokasikan untuk sebuah instance, sehingga ia akan melewati semua anggotanya dan menambahkan memori mereka untuk mencari tahu seluruh memori struct, tetapi jika kompilator menemukan sebuah instance dari struct yang sama di dalam maka ini adalah sebuah paradoks (yaitu untuk mengetahui berapa banyak memori struct A yang dibutuhkan Anda harus memutuskan berapa banyak memori struct A mengambil!).

Tetapi tipe referensi berbeda, jika struct 'A' berisi 'referensi' ke instance dari jenisnya sendiri, meskipun kita belum tahu berapa banyak memori yang dialokasikan untuk itu, kita tahu berapa banyak memori dialokasikan ke memori alamat (yaitu referensi).

HTH

m.eldehairy
sumber