Zainicjuj / zresetuj strukturę do zera / null


92
struct x {
    char a[10];
    char b[20];
    int i;
    char *c;
    char *d[10];
};

Wypełniam tę strukturę, a następnie używam wartości. W następnej iteracji chcę zresetować wszystkie pola do 0lub nullprzed ponownym użyciem.

Jak mogę to zrobić? Czy mogę użyć memsetlub muszę przejść przez wszystkich członków, a następnie zrobić to indywidualnie?

Odpowiedzi:


109

Zdefiniuj stałą statyczną instancję struktury z wartościami początkowymi, a następnie po prostu przypisz tę wartość do zmiennej, gdy chcesz ją zresetować.

Na przykład:

static const struct x EmptyStruct;

Tutaj polegam na inicjalizacji statycznej, aby ustawić moje wartości początkowe, ale możesz użyć inicjatora struktury, jeśli chcesz inne wartości początkowe.

Następnie za każdym razem za każdym razem po pętli możesz napisać:

myStructVariable = EmptyStruct;

1
@David Heffernan: czy to lepsze niż używanie, memsetjeśli chcę tylko zresetować wszystko do 0??
hari

9
@hari Sam wolałbym to podejście. Używanie memsetsprawia, że ​​czuję się brudny. Wolę, aby kompilator martwił się o układ pamięci, gdy tylko jest to możliwe. Ściśle mówiąc, takie użycie memsetnie jest przenośne, ale w praktyce zdziwiłbym się, gdybyś kiedykolwiek skompilował swój kod w dowolnym miejscu, które ma znaczenie. Więc prawdopodobnie możesz bezpiecznie używać memset, jeśli wolisz.
David Heffernan

2
@hari, jest koncepcyjnie lepsza, ponieważ podajesz domyślne wartości inicjalizacji (coś w rodzaju wzorca fabryki obiektów).
Diego Sevilla

1
@cnicutar Wiem to. Zwróć uwagę, że moja odpowiedź nie zaleca używania memset. Zwróć również uwagę na komentarz dotyczący tego, gdzie wskazuję na nieprzenośność używania memset jako środka do przypisywania wartości null. Mój poprzedni komentarz tylko wskazuje na rzeczywistość, że 0 bitów niezmiennie odpowiada zerom zmiennoprzecinkowym.
David Heffernan

1
@ kp11 citation please
David Heffernan

86

Sposobem na zrobienie czegoś takiego, gdy masz współczesne C (C99), jest użycie literału złożonego .

a = (const struct x){ 0 };

Jest to trochę podobne do rozwiązania Davida, tyle że nie musisz się martwić o zadeklarowanie pustej struktury lub zadeklarowanie jej static. Jeśli użyjesz tak constjak ja, kompilator może statycznie przydzielić złożony literał w pamięci tylko do odczytu, jeśli to konieczne.


Czy tworzy to wystąpienie x na stosie, aby następnie skopiować je do pliku? W przypadku dużych konstrukcji / małych stosów może to stanowić problem.
Étienne,

1
Podobnie jak wszystkie optymalizacje, jest to całkowicie zależne od kompilatora, więc musiałbyś sprawdzić, co produkuje twój kompilator. „Zwykle” na nowoczesnych kompilatorach tak się nie dzieje, mogą one bardzo dobrze śledzić inicjalizacje i robić tylko to, co konieczne, a nie więcej. (I nie myśl, że takie rzeczy są problemami, zanim zmierzysz prawdziwe spowolnienie. Zwykle tak nie jest.)
Jens Gustedt,

3
Ostrzeżenia „brak inicjatora” są naprawdę fałszywe. Standard C dokładnie określa, co musi się wydarzyć, i wymusza { 0 }jako domyślny inicjator. Wyłącz to ostrzeżenie, to fałszywy fałszywy alarm.
Jens Gustedt,

1
@JensGustedt Czy to typowanie jest naprawdę potrzebne? Nie mogę tego tak napisać? struct xa = (stała) {0};
Patrick,

1
@Patrick, chociaż składnia jest podobna, nie jest to rzutowanie, ale „literał złożony”. Mylisz inicjalizację i przypisanie.
Jens Gustedt

58

Lepiej niż wszystko powyżej jest kiedykolwiek użycie standardowej specyfikacji C do inicjalizacji struktury:

struct StructType structVar = {0};

Oto wszystkie bity zero (zawsze).


13
Nie sądzę jednak, abyś mógł to zrobić za każdym razem na pętli
David Heffernan

2
To rzeczywiście powinno zadziałać. Ale niestety gcc & g ++ narzekają na to. gcc generuje ostrzeżenia, a g ++ generuje błąd. Wiem, że gcc i g ++ są w tym zawinione (mają być zgodne ze specyfikacją Standard C), ale mimo to, aby zapewnić dobrą przenośność, należy wziąć to pod uwagę.
Cyjan

3
@Cyan Możesz używać {}w C ++. Twórcy gcc wydają się nie{0} być pewni, czy C ++ ma obsługiwać i sam nie jestem zaznajomiony z tą częścią standardu.
Matthew Przeczytaj

@Matthew: Tak, właściwie skończyło się na używaniu memset (), ponieważ było zbyt wiele problemów z przenośnością z {0}lub {}. Nie jestem pewien, czy standardy C i C ++ są jasne i zsynchronizowane w tym problemie, ale najwyraźniej kompilatory nie.
Cyjan

czy to zadziała w takim przypadku? struct StructType structVar = malloc (sizeof(StructType)); structVar ={0}
Sam Thomas

35

W C powszechnym idiomem jest wyzerowanie pamięci do structużycia memset:

struct x myStruct;
memset(&myStruct, 0, sizeof(myStruct));

Z technicznego punktu widzenia nie uważam, że jest to przenośne, ponieważ zakłada, że NULLwskaźnik na maszynie jest reprezentowany przez liczbę całkowitą 0, ale jest szeroko stosowany, ponieważ na większości maszyn tak jest.

Jeśli przejdziesz z C do C ++, uważaj, aby nie używać tej techniki na każdym obiekcie. C ++ czyni to legalnym tylko dla obiektów bez funkcji składowych i bez dziedziczenia.


2
Standard C stwierdza, że ​​NULL jest zawsze równe 0. Jeśli maszyna jest zaprojektowana w taki sposób, że z góry określony nieprawidłowy adres nie jest dosłownie równy 0, kompilator dla tej architektury musi dostosować się podczas kompilacji.
Kevin M

9
@KevinM Tak, symbol „0” zawsze odpowiada wskaźnikowi NULL, nawet jeśli jest zerowy; ale kompilator nic nie może zrobić, jeśli ustawisz bity na zero za pomocą memset.
Adrian Ratnapala

„C ++ sprawia, że ​​jest to dozwolone tylko w przypadku obiektów bez funkcji składowych i dziedziczenia”. Chodzi o to, czy typ jest uważany za „zwykłe stare dane”. Kryteria zależą od wersji języka C ++.
Max Barraclough

19

Jeśli masz kompilator zgodny z C99, możesz użyć

mystruct = (struct x){0};

w przeciwnym razie powinieneś zrobić to, co napisał David Heffernan, tj. zadeklarować:

struct x empty = {0};

A w pętli:

mystruct = empty;

6

Możesz użyć memsetz rozmiarem struktury:

struct x x_instance;
memset (&x_instance, 0, sizeof(x_instance));

1
Nie sądzę, żeby obsada była tutaj konieczna. Czy to jest?
templatetypedef

Cóż, jestem tak przyzwyczajony do C ++, że ... cóż, będzie działać również w C ++, więc nie uważam tego za obciążenie.
Diego Sevilla

Tak, nie byłem wystarczająco szczegółowy. To dowolny wskaźnik do kwalifikowanego T z cv można przekonwertować na void * z kwalifikacją cv. Każdy wskaźnik danych, wskaźniki funkcji to zupełnie inna sprawa. Kudos @Kevin
Marcus Borkenhagen

memsetJest to opcja, ale kiedy go używać, trzeba upewnić się, że pamięć zapisywany jest dokładnie taki sam rozmiar jak trzeci parametr, dlatego lepiej jest odwołać się do rozmiaru rzeczywistego obiektu, nie wielkość typu ty wiem, że obecnie jest. IOW memset (&x_instance, 0, sizeof(x_instance));to znacznie lepszy wybór. BTW: (void*)rzutowanie jest zbędne również w C ++.
Wolf

(przepraszam, że przegapiłem pytanie, teraz jest lepiej)
Wolf

4

Uważam, że możesz po prostu przypisać pusty set ( {}) do swojej zmiennej.

struct x instance;

for(i = 0; i < n; i++) {
    instance = {};
    /* Do Calculations */
}

To nie jest poprawne C, nawet C99. Musi to być dosłowny złożony, jak w powyższej odpowiedzi JensGustedta.
Anders


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.