C: typedef struct name {...}; VS typedef struct { ... } name;

Jak mówi tytuł, mam ten kod:

typedef struct Book{
    int id;
    char title[256];
    char summary[2048];
    int numberOfAuthors;
    struct Author *authors;
};


typedef struct Author{
    char firstName[56];
    char lastName[56];
};


typedef struct Books{
    struct Book *arr;
    int numberOfBooks;
};

Dostaję te błędy z gcc:

bookstore.c:8:2: error: unknown type name ‘Author’
bookstore.c:9:1: warning: useless storage class specifier in empty declaration [enabled by default]
bookstore.c:15:1: warning: useless storage class specifier in empty declaration [enabled by default]
bookstore.c:21:2: error: unknown type name ‘Book’
bookstore.c:23:1: warning: useless storage class specifier in empty declaration [enabled by default]

Jeśli zmienię Typ tak:

typedef struct{
    char firstName[56];
    char lastName[56];
} Author;

Wtedy nie występują żadne ostrzeżenia i błędy. Po przeszukaniu http://www.amazon.com/C-Programming-Language-2nd-Edition/dp/0131103628 i kilka godzin googlowania nie mogę pojąć, dlaczego pierwsza implementacja nie zadziała.

Author: Alek Sobczyk, 2013-07-18

4 answers

Dzieje się tu kilka rzeczy. Po pierwsze, jak powiedzieli inni, narzekanie kompilatora na Nieznany typ może wynikać z konieczności zdefiniowania typów przed ich użyciem. Ważniejsze jest jednak zrozumienie składni 3 rzeczy: (1) struct definition, (2) struct declaration i (3) typedef.

Podczas definiowania struktury, struktura może być nazwana lub nienazwana (jeśli jest nienazwana, to musi być natychmiast użyta (wyjaśni to dalej poniżej)).

struct Name {
   ...
};

Definiuje typ o nazwie "struct Name", który następnie może być użyty do zadeklarowania zmiennej struct:

struct Name myNameStruct;

To deklaruje zmienną o nazwie myNameStruct, która jest strukturą typu struct Name.

Można również zdefiniować strukturę i zadeklarować zmienną struct w tym samym czasie:

struct Name {
   ...
} myNameStruct;

Tak jak poprzednio, deklaruje zmienną o nazwie myNameStruct, która jest strukturą typu struct Name... ale robi to w tym samym czasie definiuje typ struct Name.
Na Typ może być użyty ponownie do zadeklarowania innej zmiennej:

struct Name myOtherNameStruct;

Teraz typedef jest tylko sposobem na alias typu o określonej nazwie:

typedef OldTypeName NewTypeName;

Biorąc pod uwagę powyższy typedef, za każdym razem, gdy używasz NewTypeName jest to to samo, co używasz OldTypeName. w języku programowania C jest to szczególnie przydatne w przypadku struktur, ponieważ daje możliwość pominięcia słowa "struct" przy deklarowaniu zmiennych tego typu i traktowania nazwy struktury po prostu jako typu samodzielnie (jak to robimy w C++). Oto przykład, który najpierw definiuje strukturę, a następnie typedefs struktury:

struct Name {
   ...
};

typedef struct Name Name_t;

W powyższym OldTypeName to struct Name, A NewTypeName to Name_t. Tak więc teraz, aby zadeklarować zmienną typu struct Name, zamiast pisać:

struct Name myNameStruct;

Mogę po prostu napisać:

Name_t myNameStruct;

Zauważ również, że typedef może być łączony z definicją struct i to właśnie robisz w swoim kodzie:

typedef struct {
   ...
} Name_t;

Można to również zrobić podczas nazywania struktury, ale to jest zbędne:

typedef struct Name {
   ...
} Name_t;

Zauważ dobrze: w powyższej składni, ponieważ zaczynałeś od "typedef", to całe polecenie jest instrukcją typedef, w której nazwa OldTypeName jest definicją struktury. Dlatego kompilator interpretuje nazwę nadchodzącą Po prawym nawiasie klamrowym } jako NewTypeName ... jest to , a nie nazwa zmiennej (tak jak w składni bez typedef, w którym to przypadku definiujesz strukturę i deklarujesz zmienna struct w tym samym czasie).

Co więcej, jeśli podajesz typedef, ale pomijasz Name_t na końcu, to faktycznie stworzyłeś niekompletną instrukcję typedef, ponieważ kompilator uważa wszystko w "struct Name { ... } " za OldTypeName, a ty nie podajesz NewTypeName dla typedef. To dlatego kompilator nie jest zadowolony z kodu, jaki go napisałeś (chociaż wiadomości kompilatora są raczej tajemnicze, ponieważ nie jest do końca pewien, co Ty zrobił źle).

Teraz, jak wspomniałem powyżej, jeśli nie nazwiesz typu struct w momencie jego definiowania, musisz użyć go natychmiast, aby zadeklarować zmienną:

struct {
   ...
} myNameStruct;  // declares myNameStruct as a variable with this struct
                 // definition, but the definition cannot be re-used.

Lub możesz użyć nienazwanego typu struct w typedef:

typedef struct {
   ...
} Name_t;

Ta ostateczna składnia jest tym, co naprawdę zrobiłeś, gdy napisałeś:

typedef struct{
   char firstName[56];
   char lastName[56];
} Author;

I kompilator był szczęśliwy. HTH.

Odnośnie komentarza/pytania o przyrostek _t:

_t sufiks jest konwencją, oznaczającą dla osób czytających Kod, że Nazwa symboliczna z _t jest nazwą typu (w przeciwieństwie do nazwy zmiennej). Kompilator nie analizuje ani nie jest świadomy _t.

C89, a w szczególności C99, biblioteki standardowe zdefiniowały wiele typów i zdecydowały się używać _t dla nazw tych typów. Na przykład standard C89 definiuje wzar_t, off_t, ptrdiff_t. standard C99 definiuje wiele dodatkowych typów, takich jak uintptr_t, intmax_t, int8_t, uint_least16_t, uint_fast32_t, itd. Ale _t nie jest zarezerwowany, ani specjalnie parsowany, ani zauważony przez kompilator, jest jedynie konwencją, której warto przestrzegać podczas definiowania nowych typów (poprzez typedef) w C. W C++ Wiele osób używa tej konwencji do rozpoczynania nazw typów wielkimi literami, na przykład MyNewType (w przeciwieństwie do Konwencji C my_new_type_t ). HTH

 58
Author: Daniel Goldfarb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-09 15:18:03

Składnia typedef jest następująca:

typedef old_type new_type

W pierwszej próbie zdefiniowałeś typ struct Book i nie Book. Innymi słowy, twój typ danych nazywa się struct Book, a nie Book.

W drugiej postaci użyłeś właściwej składni typedef, więc kompilator rozpoznaje typ o nazwie Book.

 5
Author: Bechir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-18 10:09:42

Musisz tylko zdefiniować autora przed zdefiniowaniem Książki.

Używasz autora w książce, więc trzeba go wcześniej zdefiniować.

 0
Author: EoiFirst,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-18 10:07:47

Myślę, że pomoże Ci to zrozumieć. http://www.tutorialspoint.com/cprogramming/c_typedef.htm

bookstore.c:8:2: error: unknown type name ‘Author’
bookstore.c:21:2: error: unknown type name ‘Book’

Są one produkowane, ponieważ musisz je zdefiniować przed ich użyciem. Przenieś strukturę "autor " i" książki "nad strukturę "Książka". To go rozwiąże.

Również ostrzeżenie, które otrzymujesz wyjaśnia, dlaczego istnieje problem, kompilator identyfikuje "typedef struct Author" jako niepotrzebne, ponieważ nie jesteś poprawnie typedef struct, więc nie ma nic przydatne dla kompilatora do "odczytu".

Ponieważ już wiesz, że odpowiedź powinna być w tej formie

typedef struct {
 ...
 ... 
 ...
} struct-name;
Trzymaj się tego.
 0
Author: aiked0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-18 10:12:51