当前位置: 首页 > 知识库问答 >
问题:

读取未初始化的值总是一种未定义的行为吗?还是有例外?

曾光远
2023-03-14

读取值时未定义行为的一个明显示例是:

int a;
printf("%d\n", a);
int i = i;     // `i` is not initialized when we are reading it by assigning it to itself.
int x; x = x;  // is this the same as above?
int y; int z = y;

共有1个答案

杜起运
2023-03-14

这三行中的每一行都触发未定义的行为。C标准中解释这一点的关键部分是关于转换的第6.3.2.1P2节:

除非是sizeof运算符的操作数、_alignof运算符、一元&运算符、++运算符、--运算符或运算符的左操作数或赋值运算符,否则将不具有数组类型的lvalue转换为存储在指定对象中的值(并且不再是lvalue);这称为lvalue转换。如果lvalue具有合格类型,则该值具有lvalue类型的不合格版本;此外,如果lvalue具有原子类型,则该值具有lvalue类型的非原子版本;否则,该值的类型为lValue。如果lvalue的类型不完整,并且没有数组类型,则该行为未定义。如果lvalue指定了一个具有自动存储持续时间的对象,该对象可以使用register存储类声明(从未使用过其地址),并且该对象未初始化(未使用初始化器声明,并且在使用之前未对其执行赋值),则该行为未定义

在这三种情况中的每一种情况下,未初始化的变量被用作赋值或初始化(为此目的,它等同于赋值)的右侧,并经历lvalue到rvalue的转换。粗体部分适用于此处,因为所讨论的对象尚未初始化。

这也适用于int i=i;大小写,因为右侧的lvalue尚未初始化。

在一个相关的问题中,有人争论int i=i;的右侧是UB,因为i的生存期还没有开始,但事实并非如此。第6.2.4节p5和P6中:

5一个对象,其标识符声明时没有链接,也没有存储类说明符statice。与一些复合文字一样,该对象具有自动存储持续时间。试图从与对象关联的线程以外的线程间接访问具有自动存储持续时间的对象的结果是实现定义的。

6对于这样一个不具有可变长度数组类型的对象,其生存期从进入与之相关联的块直到以任何方式结束该块的执行为止。(输入封闭块或调用函数会挂起(但不会结束)当前块的执行。)如果递归输入块,则每次都会创建对象的一个新实例。对象的初始值是不确定的。如果为对象指定了初始化,则每次在块的执行过程中达到声明或复合文字时都会执行初始化;否则,每次到达声明时,该值将变得不确定

因此,在本例中,i的生存期在遇到声明之前开始。因此int i=i;仍然是未定义的行为,但不是因为这个原因。

然而,6.3.2.1p2的粗体部分为使用未初始化的变量打开了大门,而不是未定义的行为,也就是说,如果所讨论的变量已经取了它的地址。例如:

int a;
printf("%p\n", (void *)&a);
printf("%d\n", a);

在本例中,如果:

  • 实现没有给定类型的陷阱表示形式,或者
  • a选择的值碰巧不是陷阱表示形式。

在这种情况下,未指定a的值。特别是,本例中的gcc和MSVC将是这种情况,因为这些实现没有整数类型的陷阱表示。

 类似资料:
  • 如果我有: 很明显,这个表达式后面的应该是零,但我看到的任何地方,他们都说这个代码的行为是未定义的,而不仅仅是的值(直到减法之前)。 很明显,编译器可以简单地在变量中使用它认为“方便”的任何垃圾值,并且它将按照预期工作。这种方法有什么问题?

  • 考虑以下C程序: null 访问易失性对象、修改对象、修改文件,或者调用执行那些操作中的任何操作的函数都是副作用,它们是执行环境状态的改变。表达式的计算通常包括值计算和副作用的启动。用于lvalue表达式的值计算包括确定指定对象的标识。 Sequenced before是单线程执行的计算之间的非对称、传递、成对关系,它导致这些计算之间的部分顺序。给定任意两个评价A和B,如果A排序在B之前,那么A的

  • 此代码为-O1和-O2提供不同的结果: 那么这是一个错误吗?或者其中是否存在某种未定义的行为,编译器有权为其提供不同的结果? 据我从C99标准中可以看出,遍历所有值的循环是有效的,因为最大无符号整数值的增量被很好地定义为导致零。 涉及无符号操作数的计算永远不会溢出,因为不能由结果无符号整数类型表示的结果将被减少为比结果类型可以表示的最大值大一的数的模。

  • 问题内容: 考虑: 那是什么原因和产生不同的结果? 问题答案: 数组构造函数创建具有给定长度的数组。它并不会创建密钥。的回调函数仅针对列表中的元素执行。 也就是说,这是与一个键(整数)0≤相关联的所有值 我 < 长度 。 的键为零,因此永远不会触发回调。 有三个键,为其执行回调函数。 [void 0, void 0, void 0].hasOwnProperty(0); // true 该规范及其