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

Will reading an unintialized value more than once always give the same value?

假设我有一段非常简单的代码(在此代码中 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 指向的内存块未初始化,因此该程序的输出可以是任何内容。

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

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

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

您可以使用 calloc 分配内存,并使用 0 初始化。但是 malloc returns 未初始化的内存地址。 它的值是不确定的

来自标准:§7.22.3.4

The malloc function allocates space for an object whose size is specified by size and whose value is indeterminate.

编辑:

OP 询问在二进制文件的特定调用中该值将始终相同吗? (这两段都阐明了这一点)

假设在未初始化的内存中有 100 并且在一些操作之后如果你没有给它分配任何东西 - 那么值将是相同的。 它是未定义的阅读它的行为。 (我们不应该认为超越了程序的范围 - 在程序中我们会知道它的价值是什么 - 通过阅读它 - 而且它是 UB 来阅读它)。

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

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

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

如果你这样做:

  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 The three types char, signed char, and unsigned char are collectively called the character types. The implementation shall define char to have the same range, representation, and behavior as either signed char or unsigned char.

来自关于类型表示的第 6.2.6 节:

Certain object representations need not represent a value of the object type. If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined. If such a representation is produced by a side effect that modifies all or any part of the object by an lvalue expression that does not have character type, the behavior is undefined.50) Such a representation is called a trap representation.

因此,如果您使用以下类型之一:

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

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

My question is the following: For every n, is the result of p[i] for all 0 <= i < n consistent? More specifically, for each read of p[i] (in the SAME invocation of the binary), will the result be the same for equivalent i?

在某些系统上是一样的。在其他人身上则不会。

通过 malloc() 分配的对象具有 "allocated" 存储持续时间和(初始)不确定值 (C2011, 7.22.3.4/2)。与这里的一些说法相反,读取不确定的值并不一定会产生未定义的行为。而且,不确定的值并不意味着它可以在程序运行时任意改变。它只是意味着该值为

either an unspecified value or a trap representation

(C2011 3.19.2/1)

问题在于陷阱表示的可能性。具体来说:

Certain object representations need not represent a value of the object type. If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined. If such a representation is produced by a side effect that modifies all or any part of the object by an lvalue expression that does not have character type, the behavior is undefined. Such a representation is called a trap representation.

(C2011, 6.2.6.1/5)

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

事情是这样的:从抽象的意义上讲,您的程序因此有可能表现出未定义的行为。如果它确实表现出未定义的行为,那么所有的赌注都会被取消,特别是,您分配的对象中的 int 值可能会在没有警告的情况下发生变化,或者看起来会发生变化。因此,C 确实不保证您的程序将打印成对的相同数字。

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

In other words, will both printf statements in a given iteration of the above loop print the same value?

不能保证。

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

在基于 x86 的系统上,您将很可能看到两次打印相同的值(假设第一次没有出现段错误)。但是,这不是您应该期望或依赖的行为 - 这是一个编码错误,句号。