Consequences of uninitialised variables: int vs unsigned char

时间:2017-08-04 12:04:46

标签: c++ initialization language-lawyer undefined-behavior

I saw the following example on cppreference.com

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

Here, int y = x; is undefined behavior because x is uninitialized.

But,

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

Here, unsigned char d = c; is indeterminate behavior, but unsigned char c; is also an uninitialized variable.

So, Why is the value of unsigned char d indeterminate?

4 个答案:

答案 0 :(得分:24)

Online references like cppreference.com are good up to a point. But it is known that sometimes errors or misinterpretations do occasionally slip through. So when dealing with such oddities, it is always a good thing to go to the official C++ standard.

N3936

§8.5 Initializers [dcl.init]

12 [...] When storage for an object with automatic or dynamic storage duration is obtained, the object has an indeterminate value , and if no initialization is performed for the object, that object retains an indeterminate value until that value is replaced (5.17). [...] If an indeterminate value is produced by an evaluation, the behavior is undefined except in the following cases:

  • If an indeterminate value of unsigned narrow character type (3.9.1) is produced by the evaluation of

    • [...]

    • the operand of a cast or conversion to an unsigned narrow character type (4.7, 5.2.3, 5.2.9, 5.4)

    • [...]

    then the result of the operation is an indeterminate value.

  • If an indeterminate value of unsigned narrow character type is produced by the evaluation of the right operand of a simple assignment operator (5.17) whose first operand is an lvalue of unsigned narrow character type, an indeterminate value replaces the value of the object referred to by the left operand

  • If an indeterminate value of unsigned narrow character type is produced by the evaluation of the initialization expression when initializing an object of unsigned narrow character type, that object is initialized to an indeterminate value.

Example:

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
}

So (to my big surprise) the standard backs this up.

As for why, the most likely reason can be also found in the standard:

§3.9.1 Fundamental types [basic.fundamental]

1 [...] For unsigned narrow character types, all possible bit patterns of the value representation represent numbers. These requirements do not hold for other types


As a side note, I just realized this can be used by an evil interviewer:

Q. Can you in a well-defined behavior change the valid value of an object to an undetermined value? If yes, how?

A.

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

答案 1 :(得分:18)

From the page you referenced: assigning from an indeterminate value is undefined behavior except

If the indeterminate value of unsigned narrow character type or std::byte is assigned to another variable with unsigned narrow character type or std::byte (the value of the variable becomes indeterminate, but the behavior is not undefined)

I believe this is because default initialization may place any combination of bits into the variable and while the standard guarantees that an unsigned narrow character type may take on values represented by every possible bit pattern, there is no such guarantee for other types.

答案 2 :(得分:1)

From the linked page:

Use of an indeterminate value obtained by default-initializing a non-class variable of any type is undefined behavior [...] except in the following cases:

...

if the indeterminate value of unsigned narrow character type or std::byte is used to initialize another variable with unsigned narrow character type or std::byte;

unsigned char is an unsigned narrow character, so this is one of the exceptions where UB does not occur.

答案 3 :(得分:0)

C的两个相关有用特性是C ++:

  1. 可以通过复制其中包含的所有单个字节来复制对象。

  2. 结构类型对象可以完全安全地复制,即使其中的某些对象不包含定义的值,只要没有尝试在整个上下文之外读取未定义的部分或其副本 - 结构复制或单字节访问。

  3. 在大多数平台上,没有特别的理由说明同样的保证也不能也不应该扩展到其他类型,但C标准的作者只是寻求定义适用于所有类型的保证。平台,C ++标准的作者只是遵循C ++行为。