2012-11-09 21 views
6

Przeczytam "Język programowania C" i napotkałem problem o typedef z struct. Kod jest tak:Reguła deklaracji w struct typedef

typedef struct tnode *Treeptr; 
typedef struct tnode { /* the tree node: */ 
    char *word; /* points to the text */ 
    int count; /* number of occurrences */ 
    struct tnode *left; /* left child */ 
    struct tnode *right; /* right child */ 
} Treenode; 

Kiedy piszemy

typedef struct tnode *Treeptr; 

tnode wciąż nie jest jeszcze ogłoszony, ale nie mamy żadnego błędu kompilacji, ale kiedy wyżej zmienić oświadczenie do:

typedef Treenode *Treeptr; 

Otrzymujemy błąd kompilacji:

error: parse error before '*' token 
warning: data definition has no type or storage class 

Co powoduje różnicę? Czy "struct tnode" nie jest takie samo jak "Treenode"?

+0

Może się okazać, że [Która część standardu C pozwala na kompilowanie tego kodu?] (Http://stackoverflow.com/questions/12200096/which-part-of-the-c-standard-allows-the-code -to-compile) i [Czy standard C uważa, że ​​istnieje jeden lub dwa typy 'struct uperms_entry' w tym nagłówku?] (http://stackoverflow.com/questions/11697705/does-the-c-standard-consider -to-there-is-one-or-two-struct-uperms-entry-typ), ale mogą być rozbite na poziomie wykraczającym poza to, gdzie się znajdujesz. –

Odpowiedz

6

Nie można użyć typu przed jego zdefiniowaniem.

Przy deklaracji typedef struct tnode { ... } Treenode; typ Treenode nie jest zdefiniowany, dopóki średnik nie zostanie osiągnięty.

Sytuacja z typedef struct tnode *Treeptr; jest inna. Mówi to kompilatorowi "istnieje typ struktury o nazwie struct tnode, a typ Treeptr jest aliasem dla wskaźnika do struct tnode". Pod koniec tej deklaracji struct tnode jest niekompletnym typem. Można tworzyć wskaźniki dla niekompletnych typów, ale nie można tworzyć zmiennych niekompletnego typu (można więc zdefiniować Treeptr ptr1; lub struct tnode *ptr2; i są one tego samego typu, ale nie można zdefiniować struct tnode node;).

Korpus struct tnode można zapisać jako:

typedef struct tnode 
{ 
    char *word; 
    int  count; 
    Treeptr left; 
    Treeptr right; 
} Treenode; 

ponieważ Treeptr jest znanym alias typu struct tnode * przed konstrukcji jest zdefiniowana. Nie można użyć wartości Treenode *left;, ponieważ Treenode nie jest znanym aliasem, dopóki nie zostanie osiągnięty ostatni średnik (w przybliżeniu).

0

Linia typedef struct tnode *Treeptr; ma domyślną deklarację forward struktury "tnode". Jest podobny do:

typedef struct tnode Treenode; 
typedef Treenode *Treeptr; 

struct tnode { /* the tree node: */ 
    char *word; /* points to the text */ 
    int count; /* number of occurrences */ 
    struct tnode *left; /* left child */ 
    struct tnode *right; /* right child */ 
}; 
1

Po zadeklarowaniu TreePtr, nie wdrażasz struktury. Jest to znane jako "deklaracja forward". Coś takiego: "tutaj używamy tego, ale później wyjaśnię to lepiej". Implementacja musi pojawić się później, tylko raz, i to znajduje się w drugim typedef.

I TreePtr to nie to samo co struktura, ponieważ TreePtr będzie w rzeczywistości nowym typem, który obejmuje fakt bycia wskaźnikiem.

+0

Dzięki, Fèlix, ładne wyjaśnienie. Więc masz na myśli 'struct undefined_struct * a;' jest dozwolone, ale powinniśmy zaimplementować 'undefined_struct' zanim powiemy' struct undefined_struct b ", czy to prawda? Ale jaka jest intencja dla tej" _dobre deklaracji "? – Rain

+1

Deklaracje przekazywania, lub niekompletne typy, mogą być pomocne w dwóch przypadkach: Możliwe jest tworzenie wzajemnie rekursywnych struktur, dzięki czemu 'struct a' zawiera wskaźnik do' struct b' i 'struct b' zawiera wskaźnik do' struct a'. To jest ezoteryczne (ale byłby poważnym problemem, gdyby nie było możliwe przekazywanie deklaracji "w przód"). "" Większym powodem dla niekompletnych typów jest to, że można zdefiniować interfejs dla zestawu funkcji w kategoriach wskaźników do niekompletnych typów, a publiczny nagłówek nigdy nie musi ujawniają to, co jest w strukturach, są "nieprzezroczystym typem" i używasz ukrywania informacji –

+0

Możesz wykonywać takie rzeczy jak 'struct undef a;', 'struct undef * b;' itp. w dowolnej kombinacji, jak tak jak potrzebujesz. Możesz włączyć e je wewnątrz innej struktury. Ale musisz "zadeklarować" implementację struktury raz, kiedy i kiedy wolisz. –