为什么 unsigned char 具有与其他数据类型不同的默认初始化行为?

Wes*_*ton 13 c++ initialization char undefined-behavior

我正在阅读有关默认初始化的 cppreference 页面,我注意到一个部分说明了以下内容:

//UB
int x;
int y = x;        
   
//Defined and ok
unsigned char c;
unsigned char d = c;
Run Code Online (Sandbox Code Playgroud)

无符号字符的相同规则也适用于 std::byte 。

我的问题是,如果您在分配之前尝试使用该值(如上例),而不是 unsigned char,那么为什么所有其他非类变量(int、bool、char 等)都会导致 UB?为什么 unsigned char 很特别?

我正在阅读的页面以供参考

eer*_*ika 9

区别不在于初始化行为。未初始化 int 的值是不确定的,默认初始化使它不确定。未初始化的 unsigned char 的值是不确定的,默认初始化使它不确定。那里没有区别。

不同之处在于生成 int 类型的不确定值的行为 - 或除异常 unsigned char 或 std::byte 之外的任何其他类型 - 是未定义的(除非该值被丢弃)。

当不确定值被正确定义时,在 C++14 中添加了unsigned char(以及后来的std::byte)的例外(尽管由于更改是一个缺陷解决方案,据我了解它适用于当时的官方标准,C++11 )。

我找不到该设计选择的文件依据。以下是定义的时间表(所有标准引述均来自草稿):

C89 - 1.6 术语定义

未定义行为 --- 行为,在使用 ... 不确定值对象时


C89 - 3.5.7 初始化 - 语义

...如果没有明确初始化具有自动存储持续时间的对象,则其值是不确定的。

任何类型都没有例外。在阅读 C++98 标准时,您会明白为什么 C 标准是相关的。

C++98 - [dcl.init]

...否则,如果没有为对象指定初始化程序,则该对象及其子对象(如果有)具有不确定的初始值

对于不确定值的含义或使用它时会发生什么,没有定义。本意可能想必已经同C89,但它是尚未。

C99 - 3. 术语、定义和符号 - 3.17.2

3.17.2 不确定值

未指定的值或陷阱表示

3.17.3 未指定值

本国际标准对在任何情况下选择哪个值没有强加要求的相关类型的有效值

注意未指定的值不能是陷阱表示。


C99 - 6.2.6 类型表示 - 6.2.6.1 总则

某些对象表示不需要表示对象类型的值。如果对象的存储值具有这样的表示形式并且被没有字符类型的左值表达式读取,则行为未定义。如果这种表示是由没有字符类型的左值表达式修改对象的全部或任何部分的副作用产生的,则行为是未定义的。41)这样的表示被称为陷阱表示。


C99 - J.2 未定义行为

在以下情况下,行为未定义:

  • ...
  • 具有自动存储期限的对象的值在不确定时使用
  • 陷阱表示由没有字符类型的左值表达式读取
  • 陷阱表示是由使用不具有字符类型的左值表达式修改对象的任何部分的副作用产生的
  • ...

C99 引入了术语陷阱表示,并且在使用时也有 UB,就像不确定值一样。字符类型(char、unsigned char 和signed char)没有陷阱表示,可用于在没有UB 的情况下对其他类型的陷阱表示进行操作。

C++ 核心语言问题 - 616.“不确定值”的定义

C++ 标准使用短语“不确定值”而没有定义它。C99 将其定义为“未指定的值或陷阱表示”。C++ 应该效仿吗?

提议的决议(2012 年 10 月):

[dcl.init] 第 12 段如下:

如果没有为对象指定初始化程序,则该对象是默认初始化的。当获取一个自动或动态存储期限的对象的存储时,该对象具有一个不确定值,如果没有对该对象进行初始化,该对象将保留一个不确定值,直到该值被替换(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]),
  • 强制转换或转换为无符号窄字符类型的操作数(4.7 [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]) 的不确定值,则该对象将被初始化为不确定值。

提议的更改被接受为具有一些进一步更改的缺陷解决方案(问题 1213),但基本保持不变(对于此问题的目的足够相似)。这就是 unsigned char 的例外似乎已被引入 C++ 的地方。据我所知,核心语言问题没有关于例外理由的公开评论或注释。