Jak zainicjować strukturę zgodnie ze standardami języka programowania C.


465

Chcę zainicjować element struct, podzielić na deklarację i inicjalizację. Oto co mam:

typedef struct MY_TYPE {
  bool flag;
  short int value;
  double stuff;
} MY_TYPE;

void function(void) {
  MY_TYPE a;
  ...
  a = { true, 15, 0.123 }
}

Czy to jest sposób na zadeklarowanie i zainicjowanie zmiennej lokalnej MY_TYPEzgodnie ze standardami języka programowania C (C89, C90, C99, C11 itd.)? A może jest coś lepszego lub przynajmniej działa?

Aktualizacja Skończyłem z statycznym elementem inicjującym, w którym ustawiam każdy podelement zgodnie z moimi potrzebami.


3
naprawdę powinieneś zaakceptować lepszą odpowiedź, widzę, że musiałeś użyć jakiegoś złego przewodnika po kodzie, ale nadal nie powinieneś sugerować innym ludziom, że to jest właściwy sposób, aby to zrobić ..
Karoly Horvath

@KarolyHorvath dobrze, większość dobrych odpowiedzi dotyczy C99. Może moje pytanie jest duplikatem stackoverflow.com/questions/6624975/… ?
cringe

gdyby to był twój pierwotny zamiar, prawdopodobnie tak, ale wtedy 1) głosy byłyby bardzo mylące. 2) z najlepszych wyników wyszukiwania jest to jedyny pokazujący sposób C99 ..... lepiej byłoby ponownie użyć tej strony do demonstracji C99 ... (podobno ludzie zaczęli linkować tę stronę, aby pokazać, jak zrób to)
Karoly Horvath

10
Ciekawe, że zaakceptowana (i mocno oceniona) odpowiedź tak naprawdę nie odpowiada na pytanie, nawet jak pierwotnie opublikowano. Wyznaczone inicjalizatory nie rozwiązują problemu PO, który polega na rozdzieleniu deklaracji od inicjalizacji. W przypadku wersji sprzed 1999 r. Jedynym prawdziwym rozwiązaniem jest przypisanie do każdego członka; dla C99 i późniejszych literał złożony, jak w odpowiedzi CesarB, jest rozwiązaniem. (Oczywiście rzeczywisty inicjator, z desygnatorami lub bez, byłby jeszcze lepszy, ale najwyraźniej OP był obarczony naprawdę złym standardem kodowania.)
Keith Thompson

31
Ściśle mówiąc, termin „ANSI C” odnosi się teraz do normy ISO 2011, która została przyjęta przez ANSI. Ale w praktyce termin „ANSI C” zwykle odnosi się do (oficjalnie przestarzałej) normy z 1989 roku. Na przykład „gcc -ansi” nadal egzekwuje standard z 1989 roku. Ponieważ to ISO opublikowało standardy 1990, 1999 i 2011, najlepiej unikać terminu „ANSI C” i odwoływać się do daty standardu, jeśli istnieje możliwość pomyłki.
Keith Thompson,

Odpowiedzi:


728

W (ANSI) C99 możesz użyć wyznaczonego inicjatora do zainicjowania struktury:

MY_TYPE a = { .flag = true, .value = 123, .stuff = 0.456 };

Edycja: Inne elementy są inicjowane jako zero: „Pominięte elementy pola są domyślnie inicjowane tak samo jak obiekty o statycznym czasie przechowywania.” ( https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html )


87
Wielkie dzieki. możesz nawet zagnieździć em: static oxeRay2f ray = {.unitDir = {.x = 1.0f, .y = 0.0f}};
orion elenzil

4
To wcale nie odpowiada na pytanie. OP chce oddzielić inicjalizację od deklaracji.
osvein

3
C99! = ANSI C - Więc to nie działa w C89, ani w C90.
Tim Wißmann

3
C99 to ANSI C, patrz to
Cutberto Ocampo

3
@CutbertoOcampo Rozumiem, co się teraz stało. Pierwotne pytanie dotyczyło rozwiązań w ANSI C, oczywiście chciał odpowiedzi tylko dla C89. W 2016 r. Zredagowano pytanie i usunięto „ANSI C”, co utrudnia obecnie zrozumienie, dlaczego w odpowiedzi i komentarzach wspomniano „(ANSI) C99”.
Étienne

198

Możesz to zrobić za pomocą złożonego literału . Według tej strony działa w C99 (który również liczy się jako ANSI C ).

MY_TYPE a;

a = (MY_TYPE) { .flag = true, .value = 123, .stuff = 0.456 };
...
a = (MY_TYPE) { .value = 234, .stuff = 1.234, .flag = false };

Oznaczenia w inicjalizatorach są opcjonalne; możesz także napisać:

a = (MY_TYPE) { true,  123, 0.456 };
...
a = (MY_TYPE) { false, 234, 1.234 };

Wyznaczone inicjalizatory są wtedy, gdy deklarujesz i definiujesz w tym samym czasie, ale literały złożone mają zastosowanie, gdy definiujesz już zadeklarowaną zmienną?
Geremia

@ Geremia musisz najpierw zdefiniować strukturę w obu przypadkach, ale dzięki wyznaczonym inicjatorom sprawiasz, że kod jest bardziej czytelny i bardziej odporny na błędy, w przypadku zmian w strukturze.
hoijui

2
To trochę trudne. Wiele razy (niektóre udane) próbowałem użyć wyznaczonych inicjatorów. Jednak dopiero teraz stało się jasne (dzięki przykładowi twojego i @ philant), że musi istnieć rzutowanie, jeśli inicjator nie jest używany podczas tworzenia obiektu. Jednak teraz dowiedziałem się również, że jeśli inicjalizatory są używane w czasie innym niż tworzenie obiektu (jak w twoim przykładzie), wówczas jest to określane jako literał złożony , a nie ściśle wyznaczony inicjator . ideone.com/Ze3rnZ
sherrellbc

93

Widzę, że już otrzymałeś odpowiedź na temat ANSI C 99, więc rzucę kość o ANSI C 89. ANSI C 89 pozwala na zainicjowanie struktury w ten sposób:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = { 5, 2.2, "George" };
    return 0;
}

Ważną rzeczą do zapamiętania, w momencie inicjalizacji choćby jednego obiektu / zmiennej w strukturze, wszystkie pozostałe zmienne zostaną zainicjalizowane do wartości domyślnej.

Jeśli nie zainicjujesz wartości w swojej strukturze, wszystkie zmienne będą zawierały „wartości śmieci”.

Powodzenia!


3
Czy w inicjalizacji można używać zmiennych?
Cyan

2
@Cyan Dotyczy obiektów z automatycznym czasem przechowywania. Zobacz 6.7.9 13) w open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf . Bo obiekty globalne są w zasadzie ograniczone do literałów. Nie możesz nawet używać innych obiektów globalnych, nawet jeśli są one stałe.
PSkocik

Tak więc jedyną różnicą w przypadku C 99 jest to, że nie można określić oznaczeń?
Akaisteph7

42

a = (MYTYPE){ true, 15, 0.123 };

sprawdziłby się w C99


1
Szczerze uważam, że odpowiedź jest najbardziej użyteczna, ale cokolwiek.
user12211554,

22

Już prawie masz ...

MY_TYPE a = { true,15,0.123 };

Szybkie wyszukiwanie w „struct initialize c” pokazuje mi to


1
Myślę, że dotyczy to standardu C, ale nie ANSI C (99?). Jestem także zobowiązany do kodowania reguł, które nie pozwolą mi zrobić deklaracji i inicjalizacji jednocześnie, więc muszę ją rozdzielić i zainicjować każdy pojedynczy podelement.
cringe

8
Inicjalizacja może nastąpić tylko w punkcie deklaracji. To właśnie oznacza „inicjowanie”. W przeciwnym razie zezwalasz, aby niezdefiniowana wartość była początkową wartością zmiennej / struktury, a następnie przypisujesz wartość później.
Kieveli

8
um ... Masz reguły kodowania, które są celowo złe? Być może powinieneś przedstawić faceta, który je napisał, w „Resource Acquisition Is Initialization” ( en.wikipedia.org/wiki/RAII )
James Curran

Zaufaj mi, naprawdę, naprawdę, nie mogę teraz zmienić trochę tych zasad. Kodowanie go jest okropne. Istnieje wiele innych reguł od lat 70., takich jak statyczne nagłówki kodu z informacjami zarządczymi, takimi jak numer wersji. Zmiany dla każdego wydania, nawet jeśli źródło się nie zmieniło ...
cringe

19

Norma języka programowania C ISO / IEC 9899: 1999 (powszechnie znana jako C99) pozwala na użycie wyznaczonego inicjatora do inicjowania elementów struktury lub unii w następujący sposób:

MY_TYPE a = { .stuff = 0.456, .flag = true, .value = 123 };

Jest zdefiniowany w paragraph 7sekcji6.7.8 Initialization normy ISO / IEC 9899: 1999 jako:

Jeśli desygnator ma formularz
. identyfikator
wówczas bieżący obiekt (zdefiniowany poniżej) będzie miał strukturę lub typ unii, a identyfikator będzie nazwą członka tego typu.

Należy zauważyć, że paragraph 9w tej samej sekcji stwierdzono, że:

O ile wyraźnie nie zaznaczono inaczej, dla celów niniejszego podrozdziału nienazwani członkowie obiektów o strukturze i typie unii nie uczestniczą w inicjalizacji. Nienazwane elementy obiektów struktury mają nieokreśloną wartość nawet po inicjalizacji.

Jednak w implementacji GNU GCC pominięte elementy są inicjowane jako wartość odpowiednia dla typu zero lub podobna do zera. Jak stwierdzono w sekcji 6.27 Wyznaczone inicjatory dokumentacji GNU GCC:

Pominięte elementy pola są domyślnie inicjowane tak samo, jak obiekty o statycznym czasie przechowywania.

Kompilator Microsoft Visual C ++ powinien obsługiwać wyznaczone inicjatory od wersji 2013, zgodnie z oficjalnym blogiem C ++ Conformance Roadmap . Akapit artykułu Initializing unions and structso inicjalizatorach w dokumentacji MSDN Visual Studio sugeruje, że nienazwani członkowie inicjują się do odpowiednich zerowych wartości, podobnie jak GNU GCC.

Norma ISO / IEC 9899: 2011 (powszechnie znana jako C11), która zastąpiła normę ISO / IEC 9899: 1999, zachowuje wyznaczone inicjatory zgodnie z sekcją 6.7.9 Initialization. Zachowuje również paragraph 9niezmienione.

Nowa norma ISO / IEC 9899: 2018 (powszechnie znana jako C18), która zastąpiła ISO / IEC 9899: 2011, zachowuje wyznaczone inicjatory w sekcji 6.7.9 Initialization. Zachowuje również paragraph 9niezmienione.


2
Początkowo zasugerowałem to jako edycję przyjętej odpowiedzi, ale została odrzucona. Dlatego zamieszczam to jako odpowiedź.
PF4Public

Uważam, że „nienazwany członek” nie oznacza „pominiętych pól”, ale raczej „anonimowych członków”, takich jak związek w struct thing { union { char ch; int i; }; };. Później standard mówi: „Jeśli na liście zamkniętej nawiasami znajduje się mniej inicjatorów, niż elementów lub elementów agregujących, lub mniej liter w łańcuchu znaków używanych do inicjalizacji tablicy o znanym rozmiarze, niż elementów w tablicy, pozostałą część agregatu inicjuje się domyślnie tak samo jak obiekty o statycznym czasie przechowywania. ”
emulacja

14

jak powiedział Ron Nuni:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = {5, 2.2, "George"};
    return 0;
}

Ważną rzecz do zapamiętania: w momencie inicjalizacji nawet jednego obiektu / zmiennej w strukturze, wszystkie pozostałe zmienne zostaną zainicjalizowane do wartości domyślnej.

Jeśli nie zainicjujesz wartości w swojej strukturze (tj. Jeśli po prostu zadeklarujesz tę zmienną), wszystkie variable.membersbędą zawierały „wartości śmieci”, tylko jeśli deklaracja jest lokalna!

Jeśli deklaracja jest globalna lub statyczna (jak w tym przypadku), wszystkie niezainicjowane variable.memberszostaną automatycznie zainicjowane, aby:

  • 0 dla liczb całkowitych i zmiennoprzecinkowych
  • '\0'dla char(oczywiście jest to tak samo jak 0ichar jest liczbą całkowitą)
  • NULL dla wskaźników.

Ciekawe o lokalnym / globalnym, dotyczy to również wartości czasu strukturalnego. Miałem lokalny i w jednym przypadku DST był włączony, w innym nie, nie z powodu niczego, co zrobiłem. Nie używałem DST (pole tm_isdst), to było od początku, ale kiedy przekonwertowałem na time_t, niektóre były o godzinę wolniejsze.
Alan Corey

3
void function(void) {
  MY_TYPE a;
  a.flag = true;
  a.value = 15;
  a.stuff = 0.123;
}

23
kiedy usłyszę „inicjalizacja różni się od przypisania”. więc czy nie jest to raczej zadanie niż inicjalizacja?
Hayri Uğur Koltuk

2

Jeśli MS nie zaktualizowało się do C99, MY_TYPE a = {true, 15,0.123};


2

Znalazłem inny sposób inicjowania struktur.

Struktura:

typedef struct test {
    int num;
    char* str;
} test;

Inicjalizacja:

test tt = {
    num: 42,
    str: "nice"
};

Zgodnie z dokumentacją GCC ta składnia jest przestarzała od GCC 2.5 .


2

Nie podobała mi się żadna z tych odpowiedzi, więc zrobiłem własną. Nie wiem, czy to ANSI C, czy nie, to tylko GCC 4.2.1 w trybie domyślnym. Nigdy nie pamiętam braketingu, więc zaczynam od podzbioru moich danych i walczę z komunikatami o błędach kompilatora, dopóki się nie zamknie. Czytelność jest moim pierwszym priorytetem.

    // in a header:
    typedef unsigned char uchar;

    struct fields {
      uchar num;
      uchar lbl[35];
    };

    // in an actual c file (I have 2 in this case)
    struct fields labels[] = {
      {0,"Package"},
      {1,"Version"},
      {2,"Apport"},
      {3,"Architecture"},
      {4,"Bugs"},
      {5,"Description-md5"},
      {6,"Essential"},
      {7,"Filename"},
      {8,"Ghc-Package"},
      {9,"Gstreamer-Version"},
      {10,"Homepage"},
      {11,"Installed-Size"},
      {12,"MD5sum"},
      {13,"Maintainer"},
      {14,"Modaliases"},
      {15,"Multi-Arch"},
      {16,"Npp-Description"},
      {17,"Npp-File"},
      {18,"Npp-Name"},
      {19,"Origin"}
    };

Dane mogą rozpoczynać życie jako plik rozdzielany tabulatorami, który należy przeszukiwać i zamieniać w coś innego. Tak, to są rzeczy Debiana. Tak więc jedna zewnętrzna para {} (wskazująca tablicę), a następnie kolejna para dla każdej struktury wewnątrz. Z przecinkami między. Umieszczanie rzeczy w nagłówku nie jest absolutnie konieczne, ale mam około 50 elementów w mojej strukturze, więc chcę je w osobnym pliku, zarówno po to, aby nie bałagan w moim kodzie, więc łatwiej jest go zamienić.


3
Nie było pytania o inicjalizację tablicy struktur! Twoja odpowiedź zawiera narzut.
Victor Signaevskyi

Chyba moim celem było zadeklarowanie struktury z już w niej zawartymi wartościami vs. użycie = do ustawienia każdej wartości później.
Alan Corey

Zasada ta obowiązuje niezależnie od tego, czy jest to jedna struktura, czy ich tablica. Używanie = nie jest tak naprawdę inicjalizacją, nie sądzę.
Alan Corey

0

Strukturę w C można zadeklarować i zainicjować w następujący sposób:

typedef struct book
{
    char title[10];
    char author[10];
    float price;
} book;

int main() {
    book b1={"DS", "Ajay", 250.0};

    printf("%s \t %s \t %f", b1.title, b1.author, b1.price);

    return 0;
}

0

Przeczytałem już dokumentację Microsoft Visual Studio 2015 dotyczącą inicjowania typów agregatów , wszystkie formy inicjowania{...} są tam wyjaśnione, ale nie ma tam mowy o inicjowaniu za pomocą kropki o nazwie „desygnator”. To też nie działa.

Standardowy C99 rozdział 6.7.8 Inicjalizacja wyjaśnia możliwości desygnatorów, ale moim zdaniem nie jest tak naprawdę jasne dla złożonych struktur. Standard C99 jako pdf .

Moim zdaniem może być lepiej

  1. Użyj = {0}; -inicjalizacji dla wszystkich danych statycznych. Kod maszynowy wymaga mniej wysiłku.
  2. Na przykład użyj makr do inicjalizacji

    typedef MyStruct_t{ int x, int a, int b; } MyStruct; define INIT_MyStruct(A,B) { 0, A, B}

Makro można dostosować, jego lista argumentów może być niezależna od zmienionej zawartości struktury. Właściwe jest, aby zainicjować mniej elementów. Jest także odpowiedni dla zagnieżdżonej struktury. 3. Prosty formularz to: Zainicjuj w podprogramie:

void init_MyStruct(MyStruct* thiz, int a, int b) {
  thiz->a = a; thiz->b = b; }

Ta procedura wygląda jak ObjectOriented w C. Użyj thiz, nie thiskompiluj jej również w C ++!

MyStruct data = {0}; //all is zero!
init_MyStruct(&data, 3, 456);

0

Szukałem fajnego sposobu na zainicjowanie mojej struktury i muszę skorzystać z poniższego (C99). To pozwala mi zainicjować pojedynczą strukturę lub tablicę struktur w taki sam sposób, jak zwykłe typy.

typedef struct {
    char *str;
    size_t len;
    jsmntok_t *tok;
    int tsz;
} jsmn_ts;

#define jsmn_ts_default (jsmn_ts){NULL, 0, NULL, 0}

Można to wykorzystać w kodzie jako:

jsmn_ts mydata = jsmn_ts_default; /* initialization of a single struct */

jsmn_ts myarray[10] = {jsmn_ts_default, jsmn_ts_default}; /* initialization of
                                                    first 2 structs in the array */

Ale to nie inicjuje tablicy struktur do wartości domyślnych. Inicjuje pierwszą strukturę w tablicy do wartości podanych w jsmn_ts_default, ale pozostałe struktury są inicjowane tak, jakby tablica miała statyczny czas przechowywania, co oznacza, że ​​elementy są inicjowane do NULLi 0. Ale jeśli #define jsmn_ts_default (jsmn_ts){"default", sizeof "default", NULL, 0}przejdziesz na to, zobaczysz, że tylko pierwszy element tablicy zostanie tak zainicjowany.
ex nihilo,

Tak, właśnie wróciłem z dalszych testów, chciałem edytować post :) btw, to samo dzieje się z int a[10] = {1};Tylko pierwszy element będzie==1
div0man
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.