c++ - 为什么 unsigned char 的默认初始化行为与其他数据类型不同?
问题描述
我正在阅读有关默认初始化的 cppreference 页面,我注意到一个部分说明了以下内容:
//UB
int x;
int y = x;
//Defined and ok
unsigned char c;
unsigned char d = c;
对于 unsigned char,同样的规则也适用于 std::byte。
我的问题是,如果您尝试在分配它之前使用该值(如上面的示例)而不是 unsigned char,为什么所有其他非类变量(int、bool、char 等)都会导致 UB?为什么 unsigned char 很特别?
解决方案
区别不在于初始化行为。uninitialised int 的值是不确定的,默认初始化使其不确定。未初始化的 unsigned char 的值是不确定的,默认初始化使其不确定。那里没有区别。
不同之处在于,产生 int 类型的不确定值(或除异常 unsigned char 或 std::byte 之外的任何其他类型)的行为是未定义的(除非该值被丢弃)。
unsigned char
当正确定义不确定值时, (以及更高版本)的异常std::byte
被添加到 C++14 中的语言中(尽管由于更改是缺陷解决方案,据我了解,它适用于当时的官方标准 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. “不确定值”的定义</p>
C++ 标准使用短语“不确定值”而不对其进行定义。C99 将其定义为“未指定的值或陷阱表示”。C++ 应该效仿吗?
提议的决议(2012 年 10 月):
[dcl.init] 第 12 段如下:
如果没有为对象指定初始化程序,则该对象是默认初始化的。当获得具有自动或动态存储持续时间的对象的存储时,该对象具有不确定的值,如果没有对该对象执行初始化,则该对象将保留一个不确定的值,直到该值被替换(5.17 [expr.ass]) . [注意:具有静态或线程存储持续时间的对象是零初始化的,请参见 3.6.2 [basic.start.init]。—尾注]如果评估产生不确定的值,则行为未定义,但以下情况除外:
- 如果通过以下评估产生无符号窄字符类型(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])的不确定值,则该对象被初始化为不确定值。
提议的更改被接受为带有一些进一步更改(issue 1213)的缺陷解决方案,但基本保持不变(对于本问题而言足够相似)。这就是 unsigned char 的异常似乎已被引入 C++ 的地方。据我所知,核心语言问题没有关于例外理由的公开评论或注释。
推荐阅读
- kotlin - 为什么这个 Koltin 代码会抛出错误 Type mismatch: inferred type is {Comparable<*>? & java.io.Serializable?} 但任何预期
- angular - 如何为父子组件添加不同的权限?
- ssl - 1 个用于多个动态 Elastic Beanstalk 环境的 SSL 证书
- python - 生成二维码,扫描时在 django 中显示 url 数据
- azure - 是否可以在不通过浏览器登录的情况下从 Azure Active Directory (AAD) 获取访问/刷新令牌?
- c - 在 C 编程中,如何 fork() 在子进程中运行 N 个函数调用?
- regex - 仅当字符在golang中的引号之外时如何拆分字符?
- java - Android 11 中的应用程序无法访问 Android/obb/package-folder 位置吗?
- c - 将两个 8 位整数组合成一个 16 位整数
- python - 如何明确告诉 python 安装命令使用哪个 python?