是否会多次读取未初始化的值总是给出相同的值?

时间:2018-01-26 18:58:12

标签: c operating-system malloc virtual-memory

假设我有一段非常简单的代码(在此代码中n = 128,但假设数组大小可以是任何代码):

#include <stdlib.h>
#include <stdio.h>

int main() {
  int *p = (int *)malloc(sizeof(int) * 128);
  for (int i = 0; i < 128; i++) {
    printf("%d\n", p[i]);
    printf("%d\n", p[i]);
  }
  return 0;
}

我理解(所以请不要在评论中提及这一点)p指向的内存块未初始化,因此该程序的输出可以是任何内容。

我的问题如下:对于每个np[i]对所有0 <= i < n一致的结果是什么?更具体地说,对于p[i]的每次读取(在二进制文件的SAME调用中),对于等效的i,结果是否相同?

换句话说,上述循环的给定迭代中的两个printf语句是否会打印相同的值?

6 个答案:

答案 0 :(得分:3)

如果你这样做:

  int *p = (int *)malloc(sizeof(int) * 128);
  for (int i = 0; i < 128; i++) {
    printf("%d\n", p[i]);
    printf("%d\n", p[i]);
  }

您调用undefined behaivor因为您读取了一个不确定的值。一旦您调用未定义的行为,所有投注都将关闭。该程序可能崩溃,它可能表现出奇怪的行为,或者它似乎可以正常工作。

因此,C标准不保证对printf的两次调用都会在上面的代码中打印相同的值,即使很多实现可能会这样。

至于为什么这是未定义的行为有点棘手。 malloc返回的字节(以及未初始化的本地人)具有不确定值。这意味着它可以是未指定的值,这意味着任何值,或陷阱表示。读取陷阱表示是导致未定义行为的原因。

陷阱表示表示不表示给定数据类型的有效值的位模式。如果读取陷阱表示,某些CPU将触发故障。

但字符类型有例外,即charsigned charunsigned char。这些类型不能具有陷阱表示。

关于类型的C standard第6.2.5节:

  

15 这三种类型charsigned charunsigned char统称为字符类型。实施应   将char定义为具有相同的范围,表示和行为   signed charunsigned char

关于类型表示的第6.2.6节:

  

某些对象表示不需要表示的值   对象类型。 如果对象的存储值具有这样的值   表示,并由没有的左值表达式读取   字符类型,行为未定义。如果是这样的表示   由副作用产生,修改对象的全部或任何部分   通过一个没有字符类型的左值表达式,   行为未定义.50)这种表示称为陷阱   表示。

因此,如果您使用以下其中一种类型:

  unsigned char *p = malloc(128);
  for (int i = 0; i < 128; i++) {
    printf("%u\n", p[i]);
    printf("%u\n", p[i]);
  }

这将是未定义的行为。这些值只是未指定而不是 indeterminate ,并且对printf的两次调用都保证打印相同的值。

答案 1 :(得分:1)

不,不会。它们的价值将是不确定的。因此每次运行可能会有所不同。在代码中使用它们将调用未定义的行为。如果它包含一些垃圾值 - 如果你想在它们之间获得一致性,这是错误的期望。不,他们不会。

您可以使用calloc分配使用0初始化的内存。但malloc返回未初始化的内存地址。 它拥有的价值是不确定的

来自标准:§7.22.3.4

  

malloc函数为大小由大小指定的对象和值 分配空间。

编辑:

OP要求在二进制文件的特定调用中该值始终相同? (两段都澄清了这一点)

假设未初始化的内存中存在100,并且在某些操作后仍然没有为其分配任何内容 - 那么值将是相同的。它&#39; s阅读它的未定义行为。 (我们不应该认为该计划的范围是正确的 - 在计划中我们会知道它的价值 - 阅读它 - 以及它的UB来阅读它。)

但没有用 - 即使这样。你不能使用它。使用它是未定义的行为。

现在你怎么知道他们是一样的?你会考虑打印它。但是如果你打印它 - 那么它将是一个未定义的行为。 读取未初始化的值是UB。(你的方式。)正确的放置方式是 - 当它是UB时行为不一致。(chux指出这一点)。

答案 2 :(得分:1)

  

我的问题如下:对于每个n,是对于所有0&lt; = i&lt; 1的p [i]的结果。一致吗?更具体地说,对于p [i]的每次读取(在二进制的SAME调用中),对于等价的i,结果是否相同?

在某些系统上,它将是相同的。在其他人,它不会。

答案 3 :(得分:1)

  

换句话说,上述循环的给定迭代中的两个printf语句都会打印相同的值吗?

无法保证。

尝试读取不确定的值会调用未定义的行为 - 允许任何结果(包括段错误)。

在基于x86的系统上,您很可能看到两次打印的相同值(假设它不会在第一个上发生段错误)。但是,这不是您应该期望或依赖的行为 - 这是一个编码错误,完全停止。

答案 4 :(得分:-1)

您看到的值只是初始化数组的解释二进制值。它可能是在同一个地方写的旧数据的幽灵。

答案 5 :(得分:-1)

通过malloc()分配的对象具有“已分配”存储持续时间和(最初)不确定值(C2011, 7.22.3.4/2)。与此处的一些声明相反,读取不确定的值并不一定会产生不确定的行为。而且,一个不确定的值并不意味着它可以在程序运行时任意改变。它只是意味着值是

  

未指定的值或陷阱表示

C2011 3.19.2/1

问题在于陷阱表示的可能性。具体做法是:

  

某些对象表示不需要表示的值   对象类型。如果对象的存储值具有这样的值   表示,并由没有的左值表达式读取   字符类型,行为未定义。如果这样的表示是   由副作用产生,修改对象的全部或任何部分   通过一个没有字符类型的左值表达式,   行为未定义。这种表示称为陷阱   表示。

C2011, 6.2.6.1/5

您的代码通过左值表达式p[i]读取不确定的值。 如果 这些值中的任何一个恰好是陷阱表示,那么您的程序的行为是未定义的。否则,标准中没有任何理由证明(未指定的)值不被正确读取和重复。

现在就是这样的事情:从抽象的意义上说,你的程序有可能表现出未定义的行为。如果它确实表现出未定义的行为,那么所有的投注都会关闭,特别是,您分配的对象中的int值可能会在没有警告的情况下发生变化,或者似乎也会这样做。因此,C确实无法保证您的程序将打印成对的相同数字。

另一方面,很少有C实现提供int类型,可以提供任何陷阱表示,而您的类型不可能是一个。如果您的实现不提供类型int的任何陷阱表示,那么分配对象的值的不确定性不会导致程序中的p[i]表达式在该实现上运行时产生未定义的行为。在具有该共同特征的符合实现的情况下,您的程序打印成对的重复值。