kendini referans yapı tanımı?


134

Çok uzun zamandır C yazmıyorum, bu yüzden bu tür yinelemeli şeyler yapmaya nasıl devam etmem gerektiğinden emin değilim ... Her hücrenin başka bir hücre içermesini istiyorum, ancak "field 'child" satırları eksik tipe sahip. Naber?

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

9
PS Aslında "struct Cell" i "Cell" (bu ortak bir model) olarak tanımlar
David Z

muhtemelen bir C ++ derleyicisi kullanıyor. eğer gerçekten C ise _Bool kullanıyor olmalı
nabiy

Gerçekten C :-) ise int kullanıyor olmalı
paxdiablo

2
Neden? C99 bool var - sadece <stdbool.h> eklemeniz gerekiyor
Jonathan Leffler

Yanıtlar:


184

Açıkça görülüyor ki bir hücre, hiç bitmeyen bir özyineleme haline geldiği için başka bir hücre içeremez.

Ancak bir hücre CAN başka bir hücreye bir işaretçi içerir.

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

7
@ cs01 Hayır, Cellhenüz kapsamda değil.
fredoverflow

1
Bu olur mantıklı. Python buna izin verir ve hatta böyle bir nesnenin serileştirilmesine bile izin verir. Neden C ++ değil?
noɥʇʎԀʎzɐɹƆ

1
Ben atamak çalıştığımda uyarıları almak Cell*için cell->child.
Tomáš Zato - Monica'yı

3
@ noɥʇʎԀʎzɐɹƆ Çünkü Python işaretçileri soyutlar, böylece onları fark etmezsiniz. structC'deki s temelde sadece tüm değerlerini yan yana depoladığından, aslında bir yapıyı kendi içinde saklamak imkansız olacaktır (çünkü bu yapı başka bir tane içermelidir ve böylece sonsuz boyutta bir hafıza yapısına yol açar) .
jazzpi

1
Kullanımının açıklaması struct Celliçin bu cevaba bakınız .
wizzwizz4

26

C'de, yapının kendisiyle birlikte oluşturduğunuz typedef'e başvuramazsınız. Aşağıdaki test programında olduğu gibi yapı adını kullanmanız gerekir:

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

Muhtemelen standartta bundan çok daha karmaşık olmasına rağmen, bunu derleyicinin struct Cellilk satırında typedefbilen ama tCellson satıra kadar bilmeyen olarak düşünebilirsiniz :-) Bu kuralı hatırlıyorum.


c ++ hakkında ne varsa lütfen c ++ ile ilgili cevapları bağlayabilirsiniz
rimalonfire

@rimiro, soru C idi. Bir C ++ varyantının cevabını istiyorsanız, bunu bir soru olarak sormalısınız .
paxdiablo

16

Teorik bakış açısına göre Diller, sadece kapsayıcı yapıları değil, sadece kendini referans alan yapıları destekleyebilir.


Pratik açıdan bakıldığında, böyle bir 'yapı hücresi' örneği gerçekten ne kadar büyük olurdu?
Marsh Ray

26
Çoğu makinede, kendisinden dört bayt daha büyük.
TonyK

13

Bunun etrafında bir yol var:

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

struct Cell;
typedef struct Cell Cell;

Bunu böyle beyan ederseniz, derleyiciye Hücre ve düz-ol'-hücresinin aynı olduğunu söyler. Böylece Cell'i normal gibi kullanabilirsiniz. Yine de, ilk bildirgenin içinde yapısal Hücre kullanmak zorunda.


9
neden struct Cell;tekrar yazdın
MAKZ

@MAKZ çünkü typedef, tanımını derlediği sırada derleyici tarafından yürütülmemiştir struct Cell.
Tyler Crompton

2
Yukarıdaki kod bloğu tek C kaynak dosyasına konur @TylerCrompton, sonra typedef etti ekstra hale "derleyici tarafından yürütülen" olmuştur struct Cell;lüzumsuz. Ancak nedense bunu dahil bir başlık dosyası içine son iki satırı koyarsanız önce tanımladığınız Cellilk dört hatları ile yapı, daha sonra ekstra struct Cell;nececairy olduğunu.
yyny

Bu, C99 standardı altında bile derlenmez.
Tomáš Zato - Monica'yı

2
@YoYoYonnY Hayır, yine de yazabilirsiniz typedef struct Cell Cell;ve bunun için Cellbir takma ad oluşturacaktır struct Cell. Derleyicinin daha struct Cell { .... }önce görüp görmediği önemli değil .
melpomene

8

Bu yazının eski olduğunu biliyorum, ancak aradığınız efekti elde etmek için aşağıdakileri denemek isteyebilirsiniz:

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

Yukarıdaki kod parçasında belirtilen iki durumdan birinde, çocuğunuzun Hücre yapısını bir işaretçi olarak bildirmeniz GEREKİR. Bunu yapmazsanız, "field 'child' türünde eksik" hatası alırsınız. Bunun nedeni, derleyicinin kullanıldığında ne kadar alan ayıracağını bilmesi için "yapı hücresi" tanımlanması gerektiğidir.

"Struct Cell" tanımının içinde "struct Cell" kullanmaya çalışırsanız, derleyici henüz "struct Cell" in ne kadar alan alması gerektiğini bilemez. Bununla birlikte, derleyici bir işaretçinin ne kadar yer kapladığını zaten bilir ve (ileri bildirimle) "Cell" in bir tür "struct Cell" olduğunu bilir (ancak bir "struct Cell" in ne kadar büyük olduğunu bilmese de ). Böylece, derleyici, tanımlanan yapı içinde bir "Hücre *" tanımlayabilir.


3

Typedef'in temel tanımından geçelim. typedef, kullanıcı tanımlı veya dahili olarak mevcut bir veri türüne bir diğer ad tanımlamak için kullanılır.

typedef <data_type> <alias>;

Örneğin

typedef int scores;

scores team1 = 99;

Buradaki karışıklık, daha önce tanımlanmayan aynı veri türünün bir üyesi nedeniyle öz referans yapısıyla ilgilidir. Yani standart şekilde kodunuzu şöyle yazabilirsiniz:

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

Ancak son seçenek genellikle yapmak istemediğimiz bazı ekstra satırları ve kelimeleri artırır (bildiğiniz kadar tembeliz;)). Bu yüzden Görünüm 2'yi tercih edin.


typedefSözdizimi ile ilgili açıklamanız yanlış (örn typedef int (*foo)(void);. Görünüm 1 ve Görünüm 2 örnekleriniz çalışmıyor: struct CellEksik bir tür oluşturduğundan, childkodunuzda gerçekten kullanamazsınız .
melpomene

3

Diğer bir uygun yöntem, yapı etiketi ile yapıyı önceden yazmaktır:

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

Kendine referans içeren bir yapı. Bağlantı listesi için bir düğümü tanımlayan bir yapıda bunun yaygın bir oluşumu. Her bir düğümün zincirdeki bir sonraki düğüme referans olması gerekir.

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

1

Önceki tüm cevaplar harika, sadece bir yapının neden kendi türünün bir örneğini (referans değil) içeremediğine dair bir fikir vermeyi düşündüm.

yapıların 'değer' türleri olduğunu, yani gerçek değeri içerdiklerini belirtmek çok önemlidir, bu nedenle bir yapıyı bildirdiğinizde, derleyicinin bir örneğine ne kadar bellek ayıracağına karar vermesi gerekir, böylece tüm üyelerinden geçer ve ekler Yapının tüm belleğini anlamaya çalışmak için belleklerini artırın, ancak derleyici aynı yapının bir örneğini bulduysa, bu bir paradokstur (yani A'nın ne kadar bellek yapısına sahip olduğunu bilmek için ne kadar bellek gerektiğine karar vermeniz gerekir. A yapısı alır!).

Ancak referans türleri farklıdır, eğer 'A' yapısı kendi türünün bir örneğine 'başvuru' içeriyorsa, ne kadar bellek tahsis edildiğini bilmememize rağmen, bir belleğe ne kadar bellek tahsis edildiğini biliyoruz adres (yani referans).

HTH

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.