Инициализация объекта на все нули - PullRequest
17 голосов
/ 15 мая 2010

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

Есть ли практическая разница между:

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

и просто

some_struct s = { 0 };

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

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

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

Редактировать: Нил Баттерворт задал этот вопрос , в продолжение, что я считаю интересным следствием этого вопроса.

Ответы [ 12 ]

23 голосов
/ 15 мая 2010

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

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

some_struct s = {};

в то время как в C идиома

some_struct s = { 0 };

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

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

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

Недостаток инициализатора = { 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 } инициализатором.

15 голосов
/ 15 мая 2010

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

6 голосов
/ 15 мая 2010

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

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

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

5 голосов
/ 15 мая 2010

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

4 голосов
/ 13 июля 2013

Я нашел хорошее решение:

template<typename T> void my_zero(T& e) {
    static T dummy_zero_object;
    e = dummy_zero_object;
}

my_zero(s);

Это правильно не только для фундаментальных типов и пользовательских типов, но и для типов, инициализируемых нулем, для которых определен конструктор по умолчанию, но не инициализирует все переменные-члены, особенно классы, содержащие нетривиальные union участников.

3 голосов
/ 15 мая 2010

Надеемся, что в настоящее время это доступно только для структур 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 голосов
/ 15 мая 2010

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

3 голосов
/ 15 мая 2010

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

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

2 голосов
/ 15 мая 2010

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

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 голосов
/ 15 мая 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...