Инициализировать / сбросить структуру до нуля / нуля


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

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

Как я могу это сделать? Могу ли я использовать memsetили мне нужно пройти через всех участников, а затем сделать это индивидуально?

Ответы:


109

Определите константный статический экземпляр структуры с начальными значениями, а затем просто присвойте это значение своей переменной всякий раз, когда вы хотите его сбросить.

Например:

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 Действительно ли нужно это приведение типов? Разве я не могу так написать? struct xa = (const) {0};
Патрик

1
@Patrick, хотя синтаксис похож, это не приведение, а «составной литерал». И вы путаете инициализацию и назначение.
Йенс Густедт

58

Лучше, чем все вышеперечисленное, когда-либо использовать стандартную спецификацию C для инициализации структуры:

struct StructType structVar = {0};

Здесь все биты нулевые (когда-либо).


13
Я не думаю, что вы можете делать это каждый раз вокруг цикла,
Дэвид Хеффернан

2
Это действительно должно работать. Но, к сожалению, gcc и g ++ жалуются на это. gcc генерирует предупреждения, а g ++ - ошибку. Я знаю, что в этом виноваты gcc и g ++ (предполагается, что они соответствуют спецификации Standard C), но, тем не менее, для хорошей переносимости необходимо учитывать такое ограничение.
Cyan

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

@Matthew: Да, на самом деле, я закончил использовать memset (), потому что было слишком много проблем с переносимостью с {0}или {}. Не уверен, что стандарты C и C ++ ясны и синхронизированы по этому вопросу, но, очевидно, компиляторы нет.
Cyan

это сработает в таком случае? struct StructType structVar = malloc (sizeof(StructType)); structVar ={0}
Сэм Томас

35

В C распространена идиома обнуления памяти для structиспользования memset:

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

Технически говоря, я не верю, что это переносимо, потому что предполагается, что NULLуказатель на машине представлен целочисленным значением 0, но он широко используется, потому что на большинстве машин это так.

Если вы переходите с C на C ++, будьте осторожны, чтобы не использовать эту технику для каждого объекта. C ++ делает это законным только для объектов без функций-членов и без наследования.


2
Стандарт C утверждает, что NULL всегда равен 0. Если машина спроектирована так, что заранее определенный недопустимый адрес не является буквально 0, компилятор для этой архитектуры должен настроить во время компиляции.
Kevin M

9
@KevinM Да, символ «0» всегда соответствует NULL-указателю, даже если он полностью равен нулю; но компилятор ничего не сможет сделать, если вы установите биты в ноль с помощью memset.
Адриан Ратнапала

«C ++ делает это законным только для объектов без функций-членов и без наследования». Вопрос в том, считается ли этот тип «старыми обычными данными». Критерии зависят от версии языка 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 ++, поэтому я не считаю это обузой.
Diego Sevilla

Да, я не был достаточно конкретным. Любой указатель на cv квалифицированный T может быть преобразован в квалифицированный cv void *. Любой указатель на данные, указатели на функции - это совсем другое дело. Kudos @Kevin
Маркус

memset- это вариант, но при его использовании вы должны убедиться, что размер памяти, в которую записывается, точно такой же, как у третьего параметра, поэтому лучше ссылаться на размер фактического объекта, а не на размер типа, который вы знаю, что это в настоящее время . IOW memset (&x_instance, 0, sizeof(x_instance));- гораздо лучший выбор. Кстати: (void*)в C ++ приведение также излишне.
Wolf

(извините, я пропустил вопрос, теперь лучше)
Wolf

4

Я считаю, что вы можете просто присвоить {}своей переменной пустой set ( ).

struct x instance;

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

Это недопустимый C, даже не C99. Это должен быть составной литерал, как в ответе ЙенсГустедта выше.
Андерс


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.