2014-08-30 49 views
10

natknąłem się na następujący kod maze definition:Cryptic definicja struct w C

typedef struct mazeNode { 
    int hasCheese; 
    int tag; 
    struct mazeNode *left; 
    struct mazeNode *right; 
} maze_t; 

maze_t maze = { 
    .tag = 1, 
    .left = &(maze_t) { 
     .left = &(maze_t) { 
      .left = &(maze_t) {}, 
      .right = &(maze_t) {} 
     }, 
     .right = &(maze_t) { 
      .right = &(maze_t) {} 
     } 
    }, 
    .right = &(maze_t) { 
     .tag = 8, 
     .left = &(maze_t) {}, 
     .right = &(maze_t) { 
      .tag = 10, 
      .left = &(maze_t) { 
       .tag = 11, 
       .left = &(maze_t) { 
        .hasCheese = 1, 
        .tag = 12 
       } 
      }, 
      .right = &(maze_t) {} 
     } 
    } 
}; 

Od połączonego blog post rozumiem, że oni próbują zdefiniować drzewo binarne z serem w diagramie.

Jednak nie wydaje mi się, aby zrobić głowę lub ogon z tego, co powinien zrobić kod C. Byłoby wspaniale, gdyby ktoś mógł mi to wyjaśnić.

+0

Przedstawiony kod opatrzony jest [post na blogu] (http://duartes.org/gustavo/blog/post/recursion/), w którym autor opisuje kod. –

+2

Używają [literałów związków] (http://stackoverflow.com/questions/23294769/initialize-struct-without-an-assignment). –

+1

'.left = & (maze_t) {};' i '.right = & (maze_t) {};' bardziej naturalnie zostaną zainicjowane na '0' lub' NULL'. W jego obecnym miejscu w hierarchii znajduje się dodatkowy element z wartością '.tag' równą 0 i wartością' .hasCheese' równą '0' (oraz zerowym lewym i prawym wskaźnikiem). –

Odpowiedz

9

Ten kod jest przy użyciu kombinacji designated initializers i compound literals, które są zarówno C99 features, ja połączony do innych odpowiedzi, gdzie świadczenia standardowych ofert dla obu tych funkcji.

przeznaczona inicjalizatory pozwalają korzystać określić konkretne pole zainicjować za pomocą .fieldname =, przykładem od połączonego dokumentu brzmi:

struct point { int x, y; }; 

następujące inicjalizacji

struct point p = { .y = yvalue, .x = xvalue }; 

jest równoważna

struct point p = { xvalue, yvalue }; 

Inną używaną funkcją są literały złożone, które są używane do tworzenia nienazwanych obiektów statycznych, a następnie kod pobiera adres tego obiektu i przypisuje mu odpowiednie wskaźniki left i right. Następnie używa tej funkcji rekursywnie w nienazwanych obiektach, aby ustawić odpowiednie wskaźniki.

.left = & (maze_t) { .... } 
      ^^^^^^^^^^^^^^^^ 
      unnamed static object 

These bezimiennych obiektów są tylko statyczne, jeżeli są wykorzystywane poza ciałem funkcji w przeciwnym razie będą musieli automatyczny czas przechowywania i przestanie istnieć po wyjściu z funkcji, a więc biorąc ich adresy jak kod robi prawdopodobnie byłoby nierozsądne.

W celach informacyjnych podaję standardową ofertę na literały złożone w moim answer here.

Ważne, aby pamiętać, że podczas korzystania z wyznaczonych inicjatorów dowolne pole nie jawnie zainicjowana będzie initialized to zero, co jest rzeczywiście ważne w tym przypadku, na przykład hasCheese zostanie ustawiony na 0 chyba że zostanie wyraźnie określone inaczej.

Chociaż są to C99 nie posiada wszystkie kompilatory obsługują lub w pełni wspierać C99 moje testy na Visual Studio pokazują, że konieczna jest wymiana pustych literały związku, na przykład:

left = &(maze_t) {} 

z NULL aby zmusić go do skompilować. Złożyłem numer bug report.

Odpowiedź do raportu o błędzie był następujący, ale w zasadzie jest to gcc/przedłużenie dzyń pracy:

Jest to rozszerzenie GNU. Clang obsługuje go jako rozszerzenie (zobacz opcję klang -Wgnu-empty-initializer).

Standardowy sposób zapisu to {0}, co spowoduje wyzerowanie wszystkich pól .

+0

to standardowa funkcja na wszystkich platformach. Czy korzystanie z takich funkcji wpływa na jakość portali? – liv2hak

+0

@ liv2hak Oba są funkcjami C99 i dlatego obsługują je zarówno 'gcc' jak i' clang'. Visual Studio ostatnio zaczęło obsługiwać funkcje C99, ale nie jestem całkowicie pewien, jak dobrze obsługiwane są te funkcje. –

+1

@ liv2hak ok, Visual Studio nie podoba się kod jak jest, więc nie w pełni przenośne. –

3

Kod inicjuje strukturę zgodnie ze składnią dozwoloną w C od 1999 (C99 i C11).

Pokrótce wyjaśniono, że można zainicjować zmienną struct, pisząc tylko "członków" struktury ujętej w nawiasy klamrowe {}.

Na przykład, biorąc pod uwagę następujące struct:

struct fractional_number_s { int numerator; unsigned int denominator; }; 

możemy zdefiniować i zainicjować zmiennych struct następująco:

struct fractional_number_s r = { .numerator = 3, .denominator = 7, }; 

Jak widać, wystarczy napisać członków, bez nazwa zmiennej r.
Ta składnia jest dozwolona w inicjalizatorach.

Również w normalnych zadań możemy mieć podobną składnię przy pomocy literałów złożonych, jak w poniższym przykładzie:

r = (struct fractional_numbers_s) { .numerator = 3, .denominator = 7 }; 

wyszukiwanie w internecie na temat tych zagadnień: C inicjalizatory struct i C literały złożone, aby uzyskać więcej informacji (uwaga techniczna: ANSI C89 nie ma tej składni, więc szukaj ISO C99 i ISO C11).