12

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

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

根据google,不确定的定义是:

  • 不确定、未知或确定
  • 令人怀疑;模糊的。

根据freedictionary,可确定的是:

  • 能够确定的

根据 merriam-webster,确定(在特定情况下)是:

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

因此,常识表明,即使在编译时不确定的值是未知的,但在运行时它是完全可确定的,例如,您总是可以读取发生在该内存位置的任何内容。

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

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

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

4

6 回答 6

17

它不确定的事实不仅意味着它在第一次读取时是不可预测的,还意味着它不能保证是稳定的。这意味着不能保证两次读取相同的未初始化变量会产生相同的值。出于这个原因,您不能通过阅读它来真正“确定”该值。(参见DR#260了解 2004 年对该主题的初步讨论,以及DR#451在 2014 年重申了这一立场。)

例如,一个变量a可能被分配以占用R1某个时间范围内的 CPU 寄存器(而不是内存位置)。为了建立最优的变量到寄存器分配计划,“对象生存期”的语言级概念不够精确。CPU 寄存器由基于更精确的“值生命周期”概念的优化编译器管理。当一个变量被赋予一个确定的值时,值的生命周期就开始了。当最后一次读取先前分配的确定值时,值生命周期结束。值生命周期确定变量与特定 CPU 寄存器相关联的时间范围。R1b. 尝试a在其值生命周期之外读取未初始化的变量实际上可能会导致读取变量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重叠,值的生命周期根本不重叠,这意味着两者都i可以j分配给同一个 CPU 寄存器。如果发生类似情况,您可能很容易发现第一个循环会i在每次迭代中打印出不断变化的值。j这与不确定的价值观念完全一致。

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

{
  int i;

  for (i = 0; i < 10; ++i)
    printf("%d\n", <future location of j>);
}

{
  int j;

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

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

于 2013-06-30T21:16:31.977 回答
11

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

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

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

[...] 在做出回应时,我们注意到对于不确定的值要求不可变位模式会减少优化机会。例如,如果包含它们的内存被调出,则需要跟踪不确定值的实际位模式。这似乎是对优化器的不必要限制,对程序员没有补偿性好处。

于 2013-06-30T21:18:05.330 回答
5

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

因此,简而言之,不,您无法阅读任何占用不确定内存的内容。

于 2013-06-30T21:20:09.883 回答
2

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

我们可以在缺陷报告 #451: Instability of uninitialized automatic variables中找到有关此问题的详细信息,该报告在提出此问题一年后提出了解决方案。

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

  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 的立场。
  • 委员会同意该领域将受益于类似于“摇摆不定”值的新定义,并且在本标准的任何后续修订中都应考虑这一点。
  • 委员会还指出,结构中的填充字节可能是“摇摆不定”表示的一种独特形式。

更新地址编辑

部分讨论包括以下评论:

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

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

于 2014-08-01T19:34:58.687 回答
1

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

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

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

于 2014-08-02T13:51:38.323 回答
-1

该标准的作者认识到,在某些情况下,确保读取不确定值的代码不会以与标准不一致的方式运行(例如,a 的值uint16_t可能不是在范围内0..65535。虽然许多实现可以廉价地提供有用的行为保证,即不确定值在比标准要求的更多情况下如何表现,但硬件平台和应用领域之间的差异意味着没有单一的保证集对所有目的都是最优的。因此,标准只是将此事作为实施质量问题。

该标准肯定会允许垃圾质量但符合标准的实现来处理几乎任何使用,例如未初始化的uint16_t作为释放鼻魔的邀请。它没有说明适合各种目的的高质量实现是否也可以这样做(并且仍然被视为适合这些目的的高质量实现)。如果需要适应旨在捕获可能的意外数据泄漏的实现,则可能需要在某些情况下显式清除对象,这些对象的值最终将被忽略,但实现无法证明它永远不会泄漏信息。同样,如果需要适应其“优化器”的设计是基于允许低质量但符合标准的实现而不是高质量的通用实现应该做的,那么这样的“优化器”

于 2018-08-28T20:13:25.453 回答