Инициализация объекта ко всем обнуляет

Часто допустимая инициализация структур данных должна обнулить всех участников. Программируя в C++, возможно, должен взаимодействовать через интерфейс с внешним API для который дело обстоит так.

Есть ли любое практическое различие между:

some_struct s;
memset(&s, 0, sizeof(s));

и просто

some_struct s = { 0 };

Люди используют обоих с методом для выбора, который более подходит для данного приложения? (Надо надеяться, подразумевается, что это только в настоящее время применимо к структурам POD; Вы получили бы все виды опустошения, если бы был станд. C++:: строка в той структуре.)

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

При создании классов и структур в C++ я склонен использовать списки инициализации; мне любопытен на предмет народных мыслей о двух "C стиль" инициализации выше, а не сравнение с тем, что доступно в C++, так как я подозреваю, что многие из нас взаимодействуют через интерфейс с библиотеками C, даже если мы кодируем главным образом в C++ сами.

Править: Neil Butterworth поставил этот вопрос в продолжении, которому я верю, интересное заключение к этому вопросу.

18
задан Community 23 May 2017 в 12:25
поделиться

11 ответов

memset практически никогда не является правильным способом. И да, есть практическая разница (см. ниже).

В C++ не все можно инициализировать литералом 0 (объекты типа enum - нельзя), поэтому в C++ распространена идиома

some_struct s = {};

а в C -

some_struct s = { 0 };

Обратите внимание, что в C = { 0 } - это то, что можно назвать универсальным нулевым инициализатором. Он может использоваться с объектами практически любого типа, поскольку {}-замкнутые инициализаторы разрешены и для скалярных объектов

int x = { 0 }; /* legal in C (and in C++) */

что делает = { 0 } полезным в общем типонезависимом коде Си (например, типонезависимые макросы).

Недостатком инициализатора = { 0 } в C89/90 и C++ является то, что его можно использовать только как часть объявления. (В C99 эта проблема устранена введением составных литералов. Аналогичная функциональность появится и в C++.) По этой причине вы можете увидеть, как многие программисты используют memset для того, чтобы обнулить что-то в середине C89/90 или C++ кода. Тем не менее, я бы сказал, что правильнее было бы обойтись без memset, а использовать что-то вроде

some_struct s;
...
{
  const some_struct ZERO = { 0 };  
  s = ZERO;
}
...

т.е. ввести "фиктивный" блок в середине кода, хотя на первый взгляд это может выглядеть не слишком красиво. Конечно, в C++ нет необходимости вводить блок.

Что касается практической разницы... Вы можете услышать от некоторых людей, что memset на практике даст те же результаты, поскольку на практике для представления нулевых значений для всех типов используется физическая схема битов "все нули". Однако, как правило, это не так. Пример, демонстрирующий разницу в типичной реализации C++, - это тип указателя на член данных

struct S;
...

int S::*p = { 0 };
assert(p == NULL); // this assertion is guaranteed to hold

memset(&p, 0, sizeof p);
assert(p == NULL); // this assertion will normally fail

Это происходит потому, что в типичной реализации обычно используется битовая схема "все-ноль" (0xFFFF... ) для представления нулевого указателя этого типа. Приведенный выше пример демонстрирует реальную практическую разницу между обнуляющим memset и обычным = { 0 } инициализатором.

23
ответ дан 30 November 2019 в 05:53
поделиться

Другой вариант - функция bzero .

#include <strings.h>
void bzero(void *s, size_t n);
0
ответ дан 30 November 2019 в 05:53
поделиться

some_struct s = { 0 }; гарантированно работает; memset полагается на детали реализации и лучше избегать.

15
ответ дан 30 November 2019 в 05:53
поделиться

Надеюсь, понятно, что в настоящее время это доступно только для структур POD; вы бы получили ошибку компилятора, если бы в этой структуре была C ++ std :: string.

Нет, вы не станете . Если вы используете memset на таком, в лучшем случае вы просто рухнете, а в худшем вы получите какую-то тарабарщину. Способ = {} может прекрасно использоваться для структур, не относящихся к POD, если они являются агрегатами. Способ = {} - лучший способ использовать C ++. Обратите внимание, что в C ++ нет причин помещать в него 0 , и это не рекомендуется, поскольку оно резко сокращает количество случаев, в которых его можно использовать

struct A {
  std::string a;
  int b;
};

int main() {
  A a = { 0 };
  A a = { };
}

Первый не будет делать то, что вы хотите : Он попытается создать std :: string из C-строки с нулевым указателем на ее конструктор. Однако второй делает то, что вы хотите: он создает пустую строку.

3
ответ дан 30 November 2019 в 05:53
поделиться

Я думаю, что инициализация намного яснее говорит о том, что вы на самом деле делаете. Вы инициализируете структуру. Когда новый стандарт будет выпущен, этот способ инициализации будет использоваться еще шире (инициализация контейнеров с помощью {} - это то, чего стоит ожидать). Метод memset немного более подвержен ошибкам и не дает четкой информации о том, что вы делаете. Это может не иметь большого значения при программировании в одиночку, но очень много значит при работе в команде.

Для тех, кто работает с C ++, memset, malloc и т. Д. довольно эзотерические существа. Я сам сталкивался с некоторыми из них.

2
ответ дан 30 November 2019 в 05:53
поделиться

В зависимости от оптимизации компилятора может быть некоторый порог, выше которого memset работает быстрее, но обычно он намного превышает нормальный размер. переменных на основе стека. Использование memset для объекта C ++ с виртуальной таблицей, конечно, плохо.

5
ответ дан 30 November 2019 в 05:53
поделиться

Единственное практическое отличие состоит в том, что синтаксис = {0}; немного яснее говорит " инициализировать его как пустое "(по крайней мере, мне это кажется более ясным).

Чисто теоретически, есть несколько ситуаций, в которых memset может выйти из строя, но, насколько мне известно, на самом деле это всего лишь теоретические ситуации. OTOH, учитывая, что он хуже как с теоретической , так и с практической точки зрения, мне трудно понять, почему кто-то захочет использовать memset для этой задачи.

3
ответ дан 30 November 2019 в 05:53
поделиться

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

3
ответ дан 30 November 2019 в 05:53
поделиться

Лучшим методом очистки структур является установка каждого поля по отдельности:

struct MyStruct
{
  std::string name;
  int age;
  double checking_account_balance;
  void clear(void)
  {
     name.erase();
     age = 0;
     checking_account_balance = 0.0;
  }
};

В приведенном выше примере определен метод clear для установки всех членов в известное состояние или значение. Методы memset и std::fill могут не работать из-за типов std::string и double.Более надежная программа очищает каждое поле индивидуально.

Я предпочитаю иметь более надежную программу, чем тратить меньше времени на набор текста.

2
ответ дан 30 November 2019 в 05:53
поделиться

В C я предпочитаю использовать {0,} вместо эквивалентного memset(). Однако gcc предупреждает о таком использовании :( Подробности здесь: http://www.pixelbeat.org/programming/gcc/auto_init.html

В C++ они обычно эквивалентны, но как всегда в C++ есть угловые случаи, которые нужно учитывать (отмеченные в других ответах).

0
ответ дан 30 November 2019 в 05:53
поделиться

Если структура содержит указатели, значение всех нулевых битов, созданное memset , может не означать то же самое, что присвоение ему 0 в коде C (или C ++). , то есть указатель NULL .

(Это также может быть случай с float и doubles , но с этим я никогда не сталкивался. Однако я не думаю, что стандарты гарантируют, что они станут равными нулю с memset тоже.)

Edit: С более прагматической точки зрения, я бы все же сказал не использовать memset , когда это возможно, чтобы избежать, поскольку это дополнительная функция звонить, писать дольше и (на мой взгляд) менее ясно по намерениям, чем = {0} .

6
ответ дан 30 November 2019 в 05:53
поделиться