Когда C++ типы POD становится инициализированным нулем?

Происходя из среды C, я всегда предполагал, что типы POD (например, ints) автоматически никогда не инициализировались нулем в C++, но кажется, что это было просто неправильный!

Мое понимание - то, что только 'явные' нестатические значения POD не становятся заполненными нулями, как показано во фрагменте кода. Я разобрался в нем и являюсь там какими-либо другими важными случаями, которые я пропустил?

static int a;

struct Foo { int a;};

void test()
{
  int b;     
  Foo f;
  int *c = new(int); 
  std::vector<int> d(1);

  // At this point...
  // a is zero
  // f.a is zero
  // *c is zero
  // d[0] is zero
  // ... BUT ... b is undefined     
}  
15
задан James McNellis 23 June 2010 в 14:02
поделиться

5 ответов

Предполагая, что вы не модифицировали a перед вызовом test(), a имеет нулевое значение, потому что объекты со статической длительностью хранения нуль-инициализируются при запуске программы.

d[0] имеет нулевое значение, потому что конструктор, вызываемый std::vector d(1), имеет второй параметр, который принимает аргумент по умолчанию; этот второй аргумент копируется во все элементы конструируемого вектора. Аргументом по умолчанию является T(), поэтому ваш код эквивалентен:

std::vector<int> d(1, int());

Вы правы, что b имеет неопределенное значение.

f.a и *c также имеют неопределенные значения. Чтобы инициализировать их по значению (что для типов POD равносильно нулевой инициализации), вы можете использовать:

Foo f = Foo();      // You could also use Foo f((Foo()))
int* c = new int(); // Note the parentheses
17
ответ дан 1 December 2019 в 03:52
поделиться

Для меня типы POD инициализируются в зависимости от того, в какую часть памяти они помещены. Ваш static int a выделен в сегменте данных, поэтому он имеет значение по умолчанию при запуске. Однако я думаю, что f не инициализирован в вашем примере ...

0
ответ дан 1 December 2019 в 03:52
поделиться

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

Сегмент данных (содержащий статические / глобальные данные и код) обычно не «повторно используется», хотя это может быть не так, если вы динамически загружаете код во время выполнения.

Память в сегменте стека постоянно используется повторно. Локальные переменные, фреймы стека функций и т. Д. Все время используются и повторно используются и не инициализируются каждый раз - только при первой загрузке приложения.

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

Так, например, если вы выделяете Foo в куче, присваиваете его полю значение, удаляете экземпляр Foo, а затем создаете новый Foo в куче, есть шанс, что новый Foo будет размещен в то же самое точное место в памяти, что и старый Foo, поэтому его поле изначально будет иметь то же значение, что и поле старого Foo.

Если задуматься, это имеет смысл, потому что ОС только инициализирует данные, чтобы предотвратить доступ одного приложения к данным из другого приложения. Предоставление приложению доступа к его собственным данным снижает риск, поэтому по соображениям производительности инициализация выполняется не каждый раз - только в первый раз, когда конкретный сегмент памяти становится доступным для использования приложением (в любом сегменте).

Иногда, когда вы запускаете приложение в режиме отладки, некоторые среды выполнения в режиме отладки инициализируют данные стека и кучи при каждом выделении (так что ваше поле Foo всегда инициализируется). Однако разные среды выполнения отладки инициализируют данные разными значениями. Некоторые инициализируются нулем, а некоторые - значением «маркера».

Дело в том, что никогда не используйте неинициализированные значения где-либо в вашем коде. Нет абсолютно никакой гарантии, что они будут инициализированы нулями. Кроме того, не забудьте прочитать ранее связанную статью о скобках и инициализации значения по умолчанию против значения, так как это влияет на определение «неинициализированного» значения.

1
ответ дан 1 December 2019 в 03:52
поделиться

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

0
ответ дан 1 December 2019 в 03:52
поделиться

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

Если я не ошибаюсь, в вашем коде:

  • a должно быть неинициализированным.
  • b должно быть неинициализированным
  • c должно указывать на новый (неинициализированный) int
  • d должно быть инициализировано в [0] (как вы правильно догадались)
1
ответ дан 1 December 2019 в 03:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: