2016-08-11 39 views
5

Dlaczego ktoś to zrobił? Jeszcze lepiej, jak to działa? Założę się, że to w jakiś sposób stworzy tablicę trzech struktur z tylko pierwszym zdefiniowanym elementem. Zdaję sobie sprawę, że wskaźnik wskazuje na pierwszy element tablicy i widzę, jak to może działać, ale jak to zdefiniowano, to mnie wyrzuca! (GCC 4.8.4)Inicjalizacja tablicy struktury inicjuje wszystkich członków jednego elementu, dlaczego?

void do_something(const void *); 

typedef struct{ 

int a; 
char b; 
int c; 

} the_data_t; 

int main(int argc, char *argv[]) 
{ 
    the_data_t my_data[] = {10, 'a', 30}; 
    do_something((const void *)my_data); 
} 

void do_something(const void *data) 
{ 
    printf("data a: %d\ndata b: %c\ndata c: %d\n", ((the_data_t*)data)->a, 
     ((the_data_t*)data)->b, ((the_data_t*)data)->c); 
} 

wyjściowy

Dane A: 10
danych B: a
Dane C: 30

Niezależnie od tego, że zmienił się do tego.

int main(int argc, char *argv[]) 
{ 
    the_data_t my_data = {10, 'a', 30}; 
    do_something(&my_data); 
} 
+1

Powinieneś pokazać prawdziwy kod. Możliwe, że funkcja pomocnicza została zaprojektowana do pracy z tablicami.Więc jeśli tablica zawiera nawet jeden element, powinieneś zadeklarować ją jako tablicę zamiast pojedynczej struktury. –

+2

Nie można zainicjować tylko części struktury, to wszystko albo nic. – molbdnilo

Odpowiedz

8

bym uznał, że w jakiś sposób stworzyć tablicę trzech struktur tylko pierwszego członu zdefiniowane

Nie, to nie w ten sposób. Zasadniczo tworzy on tablicę jednego elementu, ze wszystkimi członkami zaimplementowanymi.

Cytowanie C11 rozdział §6.7.9

Każda lista initializer klamra zamkniętych posiada powiązaną bieżący obiekt. Gdy nie ma oznaczeń , podobiekty bieżącego obiektu są inicjowane w kolejności zgodnej z z typem bieżącego obiektu: elementy tablicowe w rosnącej kolejności indeksu, struktura w porządku deklaracji i pierwszy nazwany element unii. [...]

i

Każda lista oznaczenie zaczyna swój opis z bieżącego obiektu związanego z najbliższego otoczenia pary nawiasów. Każda pozycja na liście desygnatorów (w kolejności) określa konkretny element bieżącego obiektu i zmienia bieżący obiekt dla następnego oznacznika, który ma być tym elementem. 150) Bieżący obiekt, który pojawia się na końcu listy oznaczników , jest obiektem podrzędnym inicjowanym przez następujący inicjator.

i

[...] Jeżeli inicjator od subaggregate lub zawierały związek zaczyna się od lewego nawiasu, że inicjalizatory otoczone że klamra i jego pasującego prawego nawiasu zainicjować elementów lub członkowie subagregatu lub zawartego związku. W przeciwnym razie, tylko wystarczająca liczba inicjatorów z listy jest wzięta pod uwagę dla elementów lub członków subagregatu lub pierwszego członka zawartego związku; [...]

Zasadniczo kod powinien idealnie wyglądać

the_data_t my_data[] = {{10, 'a', 30}}; 

wizualizację inicjowanie jeden element.

OTOH, czego oczekiwać można osiągnąć przez

the_data_t my_data[] = {{10}, {'a'}, {30}}; 

jeżeli tworzy tablicę 3 elementów, wszystko o zmienną składową a zainicjowany.


Powiedział,

the_data_t my_data[] = {{10, 'a', 30}}; 

jest równoważne formie pisemnej

the_data_t my_data = {10, 'a', 30}; 

wyjątkiem części, my_data nie będzie tablicą już (ale to, co dobre jest tablicą jeden element, ogólnie, albo?).

+1

"co to jest ogólnie jednowarstwowa tablica" -> Pozwala to kodowi przejść przez "odniesienie" [przykład GMP] (https://gmplib.org/list-archives/gmp-discuss/2008-March /003085.html) Zbadaj, jak zadeklarowano 'mpz_t'. – chux

+0

Ta odpowiedź jest w gruncie rzeczy poprawna, ale byłoby to o wiele jaśniejsze, gdyby przytoczyła również C2011 6.7.9/20, która wyraźnie opisuje semantykę dla przypadku, w którym nie ma żadnych nawiasów klamrowych wokół inicjalizatora elementu, jak to jest tutaj zaobserwowane. –

+0

@JohnBollinger updated, jeśli to pomaga. –

2

To nie jest właściwe. GCC wyświetli ostrzeżenie:

warning: missing braces around initializer 
warning: (near initialization for ‘my_data[0]’) 

To, co zrobi, to stworzyć tablicę z 1 elementem.

+0

GCC ostrzega, a ja się z tym zgadzam, a wraz z tobą pomijanie nawiasów wokół inicjalizatorów członków jest złym stylem. Ale to * wszystko * jest. Kod jest całkowicie poprawny w tym sensie, że jest zgodny ze standardem. –

3

Kompilator potraktuje

the_data_t my_data[] = {10, 'a', 30}; 

jak

the_data_t my_data[1] = {{ 10, 'a', 30 }}; // Though it will raise warning. 

Więc my_data jest tablicą jednego the_data_t typu.

Jest to podobne do kiedy dwuwymiarowa tablica jest zadeklarowany jak ten

int a[][3] = { 1, 2, 3 }; 

następnie kompilator będzie go traktować jako

int a[1][3] = { { 1, 2, 3 } }; 

Drukuj wielkość a a dostaniesz 12 (jeśli rozmiar z int jest 4 na tej maszynie).

+0

Najwyraźniej używany * kompilator * interpretuje inicjalizator podczas opisywania, ale mam problemy z zobaczeniem, jak to zachowanie jest opisane w standardzie. –

+0

@JohnBollinger; Jest to opisane w sekcji ** 6.7.9/20 **: * Jeśli inicjator podagregatu lub zawartego związku zaczyna się lewym nawiasem, inicjatory zamknięte przez ten nawias i jego odpowiedni prawy nawias inicjalizują elementy lub elementy subagregatu lub zawartego związku. W przeciwnym razie tylko tylu inicjatorów z listy bierze pod uwagę elementy lub członków subagregatu lub pierwszego członka zawartego związku; wszelkie pozostałe inicjatory pozostawia się do zainicjowania następnego elementu lub członka agregatu, którego obecne podagregowane lub [...]. * – haccks

2

gcc wyświetli ostrzeżenie o tym problemie, jeśli użyjesz przełącznika ostrzegawczego.

$ gcc -Wall test.c 
test.c: In function ‘main’: 
test.c:14:25: warning: missing braces around initializer [-Wmissing-braces] 
the_data_t my_data[] = {10, 'a', 30}; 
         ^
test.c:14:25: note: (near initialization for ‘my_data’) 
$ 

Aby rozwiązać ten problem albo można poprawnie zainicjować tablicę jako:

the_data_t my_data[] = {{10, 'a', 30}}; 

Albo, jak pokazałeś w poście, można zmienić my_data do zmiennej struct.

the_data_t my_data = {10, 'a', 30}; // And call do_something as 
do_something((const void *)&my_data);