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

时间:2013-06-30 21:13:50

标签: c undefined language-lawyer undefined-behavior

根据这个post,不确定的值是:

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

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

  • 不确定,已知或已确立
  • 可疑;模糊。

根据字典,可确定的是:

  • 能够确定

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

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

因此,常识规定即使在编译期间未知的不确定值是未知的,但在运行时期间它是完全可确定的,例如你可以随时阅读占用该记忆位置的任何事情。

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

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

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

6 个答案:

答案 0 :(得分:17)

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

例如,可能会指定变量a占用具有特定时间帧(而不是内存位置)的CPU寄存器R1。为了建立最佳的可变寄存器分配调度,“对象寿命”的语言级概念不够精确。 CPU寄存器由优化编译器管理,基于更精确的“值寿命”概念。当变量被赋值为确定值时,值生存期开始。当最后一次读取先前分配的确定值时,值寿命结束。值生存期确定变量与特定CPU寄存器关联的时间范围。在指定的时间范围之外,相同的寄存器R1可能与完全不同的变量b相关联。尝试在其值生命周期之外读取未初始化的变量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的对象生命周期重叠,值的生命周期也不会重叠,这意味着ij可以分配到相同的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的值。

答案 1 :(得分:11)

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

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

  

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

     

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

答案 2 :(得分:5)

C90标准清楚地表明,从不确定的位置读取是未定义的行为。更新的标准不再那么清楚(不确定的内存是“未指定的值或陷阱表示”),但编译器仍然以一种只有在读取不确定的位置是未定义的行为时才可行的方式进行优化,例如,{{ 3}}

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

答案 3 :(得分:2)

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

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

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

  1. 具有自动存储持续时间的未初始化变量(具有不具有陷阱值的类型,其地址已被采用,因此6.3.2.1p2不适用,且不是易失性)可以更改其值而无需直接操作程序?
  2. 如果问题1的答案是&#34;是&#34;,那么这种&#34;不稳定&#34;传播?
  3. 如果&#34;不稳定&#34;值可以通过函数参数传播到被调用的函数中,可以调用C标准库函数展示未定义的行为吗?
  4. 并提供了以下示例以及进一步的问题:

    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的答案是&#34;是&#34;,在所描述的条件下,未初始化的值似乎会改变其值。
    • 问题2的答案是,对不确定值执行的任何操作都会产生不确定的值。
    • 问题3的答案是,当用于不确定的值时,库函数将表现出未定义的行为。
    • 这些答案适用于所有没有陷阱表示的类型。
    • 这一观点重申了C99 DR260的立场。
    • 委员会同意这个领域将受益于类似于&#34;摇摆不定的东西的新定义。价值,这应该在本标准的任何后续修订中加以考虑。
    • 委员会还指出,结构中的填充字节可能是一种不同形式的“不稳定”。表示。

    更新地址编辑

    部分讨论包括以下评论:

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

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

答案 4 :(得分:1)

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

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

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

答案 5 :(得分:0)

该标准的作者认识到,在某些情况下,对于实现来说,要确保读取不确定值的代码不会以与标准不一致的方式运行(例如, uint16_t可能不在0..65535范围内尽管许多实现可以廉价地提供不确定的值在比标准要求更多的情况下如何行为的有用的行为保证,但是硬件平台和应用程序字段之间的差异意味着没有一个一套保证对所有目的都是最佳的,因此,该标准只是将问题视作实施质量问题。

该标准肯定会允许垃圾质量但符合要求的实现来处理几乎所有的使用,例如未初始化的uint16_t作为释放鼻恶魔的邀请。它没有说明适用于各种目的的高质量实现是否也可以做到(仍然被视为适用于这些目的的高质量实现)。如果需要容纳旨在捕获可能的意外数据泄漏的实现,则在某些情况下可能需要显式清除对象,这些对象的值最终将被忽略,但实现无法证明它永远不会泄漏信息。同样,如果需要容纳那些其“优化器”是根据允许的低质量但符合标准的实现而不是高质量的通用实现而设计的实现的,则此类“优化器”可以即使代码不关心值(从而降低效率),也有必要添加其他不必要的代码来清除对象,以避免“优化器”将失败视为错误行为的邀请。 / p>