Ініціалізуйте / скиньте структуру до нуля / нуля


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

Я заповнюю цю структуру, а потім використовую значення. На наступній ітерації я хочу скинути всі поля до 0або nullдо того, як я почну його повторно використовувати.

Як я можу це зробити? Чи можу я використати memsetабо мені доведеться пройти всіх членів, а потім зробити це окремо?

Відповіді:


109

Визначте статичний екземпляр структури const з початковими значеннями, а потім просто присвойте це значення вашій змінній, коли ви хочете її скинути.

Наприклад:

static const struct x EmptyStruct;

Тут я покладаюся на статичну ініціалізацію для встановлення моїх початкових значень, але ви можете використовувати ініціалізатор структури, якщо хочете різні початкові значення.

Потім, кожного разу, коли ви крутите цикл, ви можете написати:

myStructVariable = EmptyStruct;

1
@ Девід Хеффернан: чи це краще, ніж використовувати, memsetякщо я хочу лише скинути все на 0??
hari

9
@hari Я би віддав перевагу цьому підтвердженню сам. Використання memsetзмушує мене почуватися брудно. Я вважаю за краще дозволити компілятору турбуватися про розміщення пам'яті, де це можливо. Власне кажучи, таке використання не memsetє портативним, але на практиці я був би вражений, якщо б ви коли-небудь зібрали свій код де-небудь, що мало значення. Отже, ви, напевно, можете безпечно використовувати memset, якщо вам більше подобається.
Девід Хеффернан,

2
@hari, це концептуально краще, оскільки ви надаєте значення ініціалізації за замовчуванням (щось на зразок шаблону заводських об'єктів.)
Дієго Севілья

1
@cnicutar Я це знаю. Зауважте, що моя відповідь рекомендує не використовувати memset. Зверніть увагу також на коментар про те, де я вказую на непереносимість використання memset як засобу для присвоєння нульових значень. Мій попередній коментар просто вказує на реальність, що 0 біт незмінно відповідає нулям з плаваючою комою.
Девід Хеффернан,

1
@ kp11 цитування, будь ласка
Девід Хеффернан,

85

Спосіб зробити таке, коли у вас сучасний C (C99), - це використання складеного літералу .

a = (const struct x){ 0 };

Це чимось схоже на рішення Давида, тільки вам не потрібно турбуватися про те, щоб оголосити порожню структуру або чи оголошувати її static. Якщо ви використовуєте те саме, що constі я, компілятор може вільно розподіляти складений літерал статично у сховищі лише для читання, якщо це доречно.


Чи створює це екземпляр x у стеку, щоб потім скопіювати його в a? Для великих конструкцій / малих штабелів, що може бути проблемою.
Етьєн,

1
Як і всі оптимізації, це повністю залежить від компілятора, тому вам доведеться перевірити, що створює ваш компілятор. "Зазвичай" на сучасних компіляторах цього немає, вони можуть дуже добре відстежувати ініціалізації і робити лише те, що потрібно, не більше. (І не думайте, що подібні речі є проблемами, перш ніж виміряти реальне уповільнення. Зазвичай це не так.)
Йенс Густедт,

3
Попередження про "відсутність ініціалізатора" насправді неправдиві. Стандарт C прописує саме те, що має статися, і передбачає { 0 }ініціалізатор за замовчуванням. Вимкніть це попередження, це помилкова помилкова тривога.
Йенс Густедт,

1
@JensGustedt Чи справді потрібен такий типкастинг? Чи не можу я написати це так? структура xa = (const) {0};
Патрік

1
@Patrick, хоча синтаксис схожий, це не склад, а "складений літерал". І ви змішуєте ініціалізацію та призначення.
Йенс Густедт,

58

Краще, ніж усе вище, будь-коли використовувати специфікацію C для ініціалізації структури:

struct StructType structVar = {0};

Тут усі біти нульові (коли-небудь).


13
Я не думаю, що ви можете це робити щоразу, коли відбувається петелька
Девід Хеффернан,

2
Це справді припустимо, що спрацює. Але, на жаль, gcc & g ++ скаржаться на це. gcc генерує попередження, тоді як g ++ генерує помилку. Я знаю, що gcc & g ++ винні в цьому (вони повинні дотримуватися специфікації Standard C), але тим не менше, для гарної переносимості, це обов'язкове врахування такого обмеження.
блакитний

3
@Cyan Ви можете використовувати {}в C ++. Розробники gcc, здається, не впевнені, чи повинен підтримувати C ++,{0} і я сам не знайомий з цією частиною стандарту.
Метью Прочитав

@Matthew: Так, насправді, я в кінцевому підсумку використовував memset (), тому що було занадто багато проблем з портативністю {0}або {}. Не впевнений, що стандарти C & C ++ чіткі та синхронізовані щодо цього питання, але, мабуть, компілятори - ні.
Cyan

чи спрацює це в такому випадку? struct StructType structVar = malloc (sizeof(StructType)); structVar ={0}
Сем Томас

34

У C загальна ідіома обнулення пам'яті для structвикористання memset:

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

Технічно кажучи, я не вважаю, що це портативно, оскільки передбачає, що NULLвказівник на машині представлений цілим значенням 0, але він широко використовується, оскільки на більшості машин це саме так.

Якщо ви переходите з C на C ++, будьте обережні, щоб не використовувати цей прийом на кожному об’єкті. C ++ робить це законним лише для об'єктів, які не мають функцій-членів і не мають спадку.


2
Стандарт C стверджує, що NULL завжди дорівнює 0. Якщо машина спроектована таким чином, що заздалегідь визначена недійсна адреса не є буквально 0, компілятор для цієї архітектури повинен налаштуватися під час компіляції.
Kevin M

8
@KevinM Так, символ "0" завжди відповідає показнику NULL, навіть якщо він дорівнює нулю; але компілятор нічого не може зробити, якщо ви встановите біти на нуль за допомогою memset.
Адріан Ратнапала,

"C ++ робить це законним лише для об'єктів, які не мають функцій-членів і не мають спадку." Мова йде про те, чи вважається тип «простими старими даними». Критерії залежать від версії мови С ++.
Макс Барракло

19

Якщо у вас є компілятор, сумісний із C99, ви можете використовувати

mystruct = (struct x){0};

інакше ви повинні робити те, що написав Девід Хеффернан, тобто заявляти:

struct x empty = {0};

І в циклі:

mystruct = empty;

6

Ви можете використовувати memsetз розміром структури:

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

1
Я не думаю, що тут потрібен акторський склад. Є це?
templatetypedef

Ну, я настільки звик до C ++, що ... ну, це буде працювати і в C ++, тому я не бачу в ньому тягаря.
Дієго Севілья

Так, я був недостатньо конкретним. Це будь-який вказівник на cv кваліфікований T може бути перетворений на cv кваліфікований void *. Будь-який покажчик даних, покажчики функцій - це зовсім інша справа. Слава @Kevin
Marcus Borkenhagen

memsetє варіантом, але, використовуючи його, ви повинні переконатися, що записана пам’ять має точно такий самий розмір, як третій параметр, тому краще посилатися на розмір фактичного об’єкта, а не на розмір типу, який ви знаю, що це зараз . IOW memset (&x_instance, 0, sizeof(x_instance));- набагато кращий вибір. До речі: (void*)акторський склад також зайвий у C ++.
Вольф

(вибачте, я пропустив доглянути питання, зараз краще)
Вольф

5

Я вважаю, що ви можете просто призначити порожній набір ( {}) для вашої змінної.

struct x instance;

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

0
 struct x myX;
 ...
 memset(&x, 0, sizeof(myX));

5
Я думаю, OP знає, як зателефонувати до memset, але швидше за все, справа в тому, мудро це робити чи ні
David Heffernan

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.