C – 是不确定的不确定值吗?

根据这篇文章 ,不确定的价值是:

3.17.2 1 indeterminate value either an unspecified value or a trap representation 

根据谷歌的说法,不确定性的定义是:

  • 不确定,已知或已建立
  • 遗憾; 模糊。

根据字典,可确定的是:

  • 能够被确定

根据merriam-webster,确定(在特定的上下文中)是:

  • 通过调查,推理或计算找出或做出决定

因此,常识规定尽管在编译期间不确定值是未知的,但它在运行时是完全可确定的,例如,您总是可以读取占用该内存位置的任何事件。

或者我错了? 如果是这样,为什么?

编辑:为了澄清,我发布了与用户试图说服不确定值不确定的用户的激烈争论,我非常怀疑。

编辑2:澄清,通过“可确定”我并不意味着稳定或可用的值,即使它是未初始化的内存的垃圾值,仍然可以确定该垃圾的价值。 我的意思是试图确定这个价值仍然会产生一些价值,而不是……没有行动。 所以这个值必须来自一些内存,分配为仍然不确定的值的存储,我高度怀疑编译器实际上会使用一个随机数生成器只是为了提出一些任意值。

它不确定的事实不仅意味着它在第一次读取时是不可预测的,它还意味着它不能保证稳定。 这意味着两次读取相同的未初始化变量并不能保证产生相同的值。 因此,您无法通过阅读来“确定”该值。 (参见DR#260 ,从2004年开始对该主题进行初步讨论, DR#451在2014年重申该位置。)

例如,可以指定变量a以占用具有特定时间帧(而不是存储器位置)的CPU寄存器R1 。 为了建立最佳的可变寄存器分配调度,“对象寿命”的语言级概念不够精确。 CPU寄存器由优化编译器管理,基于更精确的“值寿命”概念。 当变量被赋值为确定值时,值生存期开始。 当最后一次读取先前分配的确定值时,值寿命结束。 值生存期确定变量与特定CPU寄存器关联的时间范围。 在指定的时间帧之外,相同的寄存器R1可能与完全不同的变量b相关联。 试图在其值生命周期之外读取未初始化的变量可能实际上导致读取变量b ,这可能是主动变化的。

在此代码示例中

 { int i, j; for (i = 0; i < 10; ++i) printf("%d\n", j); for (j = 0; j < 10; ++j) printf("%d\n", 42); } 

编译器可以很容易地确定即使ij对象生命周期重叠,值的生命周期也根本不重叠,这意味着ij都可以分配给同一个CPU寄存器。 如果发生类似的事情,您可能很容易发现第一个循环在每次迭代时打印不断变化的i值。 这与j不确定的价值观念完全一致。

请注意,此优化不一定需要CPU寄存器。 另一个例子,一个关注保留有价值的堆栈空间的智能优化编译器可能会分析上面代码示例中的值生存期并将其转换为

 { int i; for (i = 0; i < 10; ++i) printf("%d\n", ); } { int j; for (j = 0; j < 10; ++j) printf("%d\n", 42); } 

变量ij在不同时间占用内存中的相同位置。 在这种情况下,第一个循环可能会再次在每次迭代时打印i的值。

连续两次读取不确定值可以给出两个不同的值。 此外,读取不确定的值会在陷阱表示的情况下调用未定义的行为。

在DR#260中 ,C委员会写道:

不确定的值可以由任何位模式表示。 C标准规定不要求对代表给定值的位的两次检查将观察到相同的位模式,只是每次的观察模式将是该值的有效表示。

[…]在达到我们的响应时,我们注意到要求不确定值的不可变位模式会减少优化机会。 例如,如果包含它们的内存被分页,则需要跟踪不确定值的实际位模式。 这对优化程序来说似乎是一种不必要的限制,对程序员没有任何补偿性好处。

C90标准清楚地表明,从不确定的位置读取是不确定的行为。 更新的标准不再那么清楚(不确定的内存是“未指定的值或陷阱表示”),但编译器仍然以一种只有在读取不确定的位置是未定义的行为时才可行的方式进行优化,例如, 乘以未初始化变量中的整数乘以2会产生奇数结果 。

所以,简而言之,不,你无法阅读任何占据不确定记忆的事情。

我们无法确定不确定值的值,即使在通常会导致可预测值(例如乘以零)的操作下也是如此。 根据提出的新语言,该值是不稳定的 (参见编辑)。

我们可以在缺陷报告#451中找到有关此问题的详细信息:未初始化的自动变量的不稳定性,在提出此问题后一年内提出了解决方案。

此缺陷报告涵盖了与您的问题非常相似的内容。 解决了三个问题:

  1. 具有自动存储持续时间的未初始化变量(具有不具有陷阱值的类型,其地址已被采用,因此6.3.2.1p2不适用且不易变)可以在不直接执行程序的情况下更改其值吗?
  2. 如果问题1的答案是“是”,那么这种“不稳定性”会传播多远?
  3. 如果“不稳定”值可以通过函数参数传播到被调用函数中,那么调用C标准库函数是否会出现未定义的行为?

并提供了以下示例以及进一步的问题:

 unsigned char x[1]; /* intentionally uninitialized */ printf("%d\n", x[0]); printf("%d\n", x[0]); 

标准是否允许实现让此代码打印两个不同的值? 如果是这样,如果我们插入以下三个语句中的任何一个

 x[0] = x[0]; x[0] += 0; x[0] *= 0; 

在声明和printf语句之间,这种行为仍然允许吗? 或者,这些printf语句可以表现出未定义的行为,而不必打印合理的数字。

提议的决议似乎不太可能有太大变化:

  • 问题1的答案是“是”,在所述条件下的未初始化值似乎可以改变其价值。
  • 问题2的答案是,对不确定值执行的任何操作都将具有不确定的值。
  • 问题3的答案是,当在不确定的值上使用时,库函数将表现出未定义的行为。
  • 这些答案适用于没有陷阱表示的所有类型。
  • 这一观点再次肯定了C99 DR260的立场。
  • 委员会同意,该领域将受益于类似于“摇摆”值的新定义,并且应在本标准的任何后续修订中予以考虑。
  • 该委员会还指出,结构内的填充字节可能是“摇摆”表示的一种不同forms。

更新以进行地址编辑

部分讨论包括以下评论:

  • 部分基于制定附件L的先前经验,形成了强烈的情绪,需要一种新的“摇摆”价值。 潜在的问题是现代编译器跟踪价值传播,并且在合成用于后续使用的不同值之前,可以丢弃为对象的初始使用而合成的未初始化值作为无关紧要的值。 否则要求失败重要的编译器优化。 “摇摆”值的所有使用可能被视为未定义的行为。

因此,您将能够确定一个值,但值可能会在每次评估时发生变化。

当标准引入类似不确定的术语时,它是一个规范性术语:标准的定义适用,而不是字典定义。 这意味着不确定的值不会多于或少于未指定的值或陷阱表示。 不确定的普通英语含义不适用。

即使是标准中未定义的术语,也可能是规范性的,包括规范性参考文献。 例如,C99标准的第2部分规范性地包括一个名为ISO / IEC 2382-1:1993的文件, 信息技术 – 词汇 – 第1部分:基本术语。

这意味着如果在标准中使用了一个术语,并且没有在文本中定义(未以斜体和术语引入,并且未在术语部分中给出),则它可能仍然是上述词汇文档中的一个词; 在这种情况下,该标准的定义适用。

标准的作者认识到,在某些情况下,实现可能会花费昂贵,以确保读取不确定值的代码不会以与标准不一致的方式运行(例如, uint16_t的值可能不会虽然许多实现可以廉价地提供关于不确定值如何在比标准要求的更多情况下表现的有用行为保证,但硬件平台和应用领域之间的变化意味着没有一套保证对于所有人都是最佳的。因此,标准只是将此事作为执行质量问题。

该标准肯定会允许垃圾质量但符合要求的实施几乎任何使用例如未初始化的uint16_t作为释放鼻子恶魔的邀请。 它没有说明适用于各种目的的高质量实现是否也可以这样做(并且仍被视为适合这些目的的高质量实现)。 如果需要适应旨在捕获可能的非预期数据泄漏的实现,则可能需要在某些情况下明确地清除对象,在这些情况下,它们的值最终会被忽略,但实现无法certificate它永远不会泄漏信息。 同样,如果需要适应其“优化器”是基于允许低质量但符合要求的实现而设计的实现,而不是高质量通用实现应该做什么,这样的“优化器”可能使得有必要添加其他不必要的代码来清除对象,即使代码不关心值(从而降低效率),以避免让“优化器”将失败视为无意义行为的邀请。