Подтвердить что ты не робот

Последствия неинициализированных переменных: int vs unsigned char

Я увидел следующий пример cppreference.com

int x;     // OK: the value of x is indeterminate
int y = x; // undefined behavior

Здесь int y = x; - это undefined поведение, потому что x не инициализируется.

Но,

unsigned char c;     // OK: the value of c is indeterminate
unsigned char d = c; // OK: the value of d is indeterminate

Здесь unsigned char d = c; является неопределенным поведением, но unsigned char c; также является неинициализированной переменной.

Итак, Почему значение unsigned char d неопределенно?

4b9b3361

Ответ 1

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

N3936

§8.5 Инициализаторы [dcl.init]

12 [...] При хранении для объекта с автоматическим или динамическим хранилищем длительность, объект имеет неопределенное значение, и если для объекта не выполняется инициализация, этот объект сохраняет неопределенное значение до тех пор, пока это значение не будет заменено (5.17). [...] Если неопределенное значение получается путем оценки, поведение undefined за исключением следующих случаев:

  • Если неопределенное значение беззнакового типа узкого символа (3.9.1) получается путем оценки

    • [...]

    • операнд приведения или преобразование в неподписанный тип узкого символа (4.7, 5.2.3, 5.2.9, 5.4)

    • [...]

    то результатом операции является неопределенное значение.

  • Если неопределенное значение типа беззнакового узкого символа создается путем вычисления правильного операнда простого присваивания оператор (5.17), первым операндом которого является lзначение беззнакового узкого тип символа, неопределенное значение заменяет значение объект, на который ссылается левый операнд

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

Пример:

int f(bool b) {
  unsigned char c;
  unsigned char d = c; // OK, d has an indeterminate value
  int e = d; // undefined behavior
  return b ? d : 0; // undefined behavior if b is true
}

Итак (к моему большому удивлению) стандарт поддерживает это.

Что касается причин, то наиболее вероятная причина также может быть найдена в стандарте:

§3.9.1 Основные типы [basic.fundamental]

1 [...] Для неподписанных типов узких символов все возможные битовые шаблоны представления значений представляют числа. Эти требования не выполняются для других типов


Как побочная заметка, я просто понял, что это может использовать злой интервьюер:

Q. Можете ли вы в четко определенном поведении изменить действительное значение объекта на неопределенное значение? Если да, то как?

а.

unsigned char ind;
unsigned char x = 24;
x = ind; // x had a valid value, now x has an indetermined value

Ответ 2

На странице, на которую вы ссылаетесь: назначение из неопределенного значения - undefined, кроме

Если неопределенное значение беззнакового узкого символьного типа или std:: byte назначается другой переменной с неподписанным узким типом символов или std:: byte (значение переменной становится неопределенным, но поведение не undefined)

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

Ответ 3

От связанная страница:

Использование неопределенного значения, полученного по умолчанию - инициализация переменной, отличной от класса любого типа, - это поведение undefined [...], за исключением следующих случаев:

...

если неопределенное значение беззнакового узкого символьного типа или std:: byte используется для инициализации другой переменной беззнаковым узким символьным типом или std:: byte;

unsigned char - непознанный узкий символ, поэтому это одно из исключений, когда UB не встречается.

Ответ 4

Двумя полезными функциями C, переносимыми на С++, являются:

  • Объекты можно копировать, копируя все содержащиеся в нем отдельные байты.

  • Объекты структурного типа могут быть надежно скопированы целиком, даже если некоторые из объектов в нем не содержат определенных значений, при условии, что не предпринимаются попытки прочитать разделы undefined или их копии вне контекста копирование всей структуры или индивидуально-байтовый доступ.

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