且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

C ++标准是否改变了在C ++ 14中使用不确定值和未定义的行为?

更新时间:2023-11-13 08:18:52

是的,这种变化是由语言的变化驱动的如果通过评估产生了不确定的值,但对于无符号的窄字符有一些例外,则会使其未定义行为。



缺陷报告 1787 ,其建议文字可在 N3914 中找到 1 是最近在2014年获得接受并且纳入最新工作草案 N3936



对于不确定值最有趣的变化是 8.5 $ c>段落 12


如果没有为一个对象指定初始化程序,则该对象被默认初始化;如果未执行初始化,则具有自动或动态存储持续时间的对象具有不确定的值。 [注意:具有静态或线程存储持续时间的对象为零初始化,请参见3.6.2。 - 结束注释]

):






如果没有为对象指定初始化程序,则对象为
default-initialized。当获得具有自动或
动态存储持续时间的对象的存储时,对象具有不确定的
,并且如果没有对对象执行初始化,那么
对象保留一个不确定的值,直到该值被替换
(5.17 [expr.ass])。 [注意:静态或线程存储
持续时间的对象是零初始化的,请参见3.6.2 [basic.start.init]。 -end
note] 如果评估产生不确定的值,则
行为是未定义的,除非在以下情况




  • 如果通过以下评估产生无符号窄字符类型的不确定值(3.9.1 [basic.fundamental]):




    • 条件表达式的第二或第三个操作数(5.16 [expr.cond]),


    • 一个逗号的正确操作数(5.18 [expr.comma]),


    • 一个转换的操作数或转换为无符号窄字符类型conv.integral],5.2.3 [expr.type.conv],5.2.9
      [expr.static.cast],5.4 [expr.cast])或


    • 一个废值表达式(第5条[expr]),





  • 如果无符号窄字符类型(3.9.1 [basic.fundamental])的不确定值由评估一个简单赋值运算符(5.17 [expr.ass])的正确
    操作数,其第一个
    操作数是无符号窄字符类型的左值,
    不确定值替换了


  • 如果无符号窄字符类型的不确定值(3.9.1 [basic.fundamental ])通过在初始化无符号
    窄字符类型的对象时初始化
    初始化表达式产生,该对象被初始化为不确定的
    值。



并包含以下示例:




[示例:



  
unsigned char c;
unsigned char d = c; // OK,d有一个不确定的值
int e = d; // undefined behavior
return b? d:0; //未定义的行为如果b是真的
}

- ]


我们可以在 N3936 ,这是目前的工作草稿 N3937 C ++ 14 DIS





有趣的是,在这个草案之前不像C 它一直有一个明确的概念,未定义值的用法未定义 C ++使用术语不确定值,甚至不定义它( 假设我们不能从C99中借用定义 )以及请参阅缺陷报告616 。我们不得不依赖于低于指定的左值到右值转换,在草稿C + +11标准 4.1 段落的部分中说明:



<...> p>






脚注:


  1. 1787 缺陷报告616 ,我们可以在 N3903


As covered in Does initialization entail lvalue-to-rvalue conversion? Is int x = x; UB? the C++ standard has a surprising example in section 3.3.2 Point of declaration in which an int is initialized with it's own indeterminate value:

int x = 12;
{ int x = x; }

Here the second x is initialized with its own (indeterminate) value. — end example ]

Which Johannes answer to this question indicates is undefined behavior since it requires an lvalue-to-rvalue conversion.

In the latest C++14 draft standard N3936 which can be found here this example has changed to:

unsigned char x = 12;
{ unsigned char x = x; }

Here the second x is initialized with its own (indeterminate) value. — end example ]

Has something changed in C++14 with respect to indeterminate values and undefined behavior that has driven this change in the example?

Yes, this change was driven by changes in the language which makes it undefined behavior if an indeterminate value is produced by an evaluation but with some exceptions for unsigned narrow characters.

Defect report 1787 whose proposed text can be found in N39141 was recently accepted in 2014 and is incorporated in the latest working draft N3936:

The most interesting change with respect to indeterminate values would be to section 8.5 paragraph 12 which goes from:

If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an object with automatic or dynamic storage duration has indeterminate value. [ Note: Objects with static or thread storage duration are zero-initialized, see 3.6.2. — end note ]

to (emphasis mine):

If no initializer is specified for an object, the object is default-initialized. 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 [expr.ass]). [Note: Objects with static or thread storage duration are zero-initialized, see 3.6.2 [basic.start.init]. —end note] 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 [basic.fundamental]) is produced by the evaluation of:

    • the second or third operand of a conditional expression (5.16 [expr.cond]),

    • the right operand of a comma (5.18 [expr.comma]),

    • the operand of a cast or conversion to an unsigned narrow character type (4.7 [conv.integral], 5.2.3 [expr.type.conv], 5.2.9 [expr.static.cast], 5.4 [expr.cast]), or

    • a discarded-value expression (Clause 5 [expr]),

    then the result of the operation is an indeterminate value.

  • If an indeterminate value of unsigned narrow character type (3.9.1 [basic.fundamental]) is produced by the evaluation of the right operand of a simple assignment operator (5.17 [expr.ass]) 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 (3.9.1 [basic.fundamental]) 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.

and included the following example:

[ 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
}

end example ]

We can find this text in N3936 which is the current working draft and N3937 is the C++14 DIS.

Prior to C++1y

It is interesting to note that prior to this draft unlike C which has always had a well specified notion of what uses of indeterminate values were undefined C++ used the term indeterminate value without even defining it (assuming we can not borrow definition from C99) and also see defect report 616. We had to rely on the underspecified lvalue-to-rvalue conversion which in draft C++11 standard is covered in section 4.1 Lvalue-to-rvalue conversion paragraph 1 which says:

[...]if the object is uninitialized, a program that necessitates this conversion has undefined behavior.[...]


Footnotes:

  1. 1787 is a revision of defect report 616, we can find that information in N3903