多次读取未初始化的值是否总是会给出相同的值?
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 < n
的p[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 将触发故障。
但是字符类型有一个例外,即 char
、signed char
和 unsigned 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
问题在于陷阱表示的可能性。具体来说:
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.
您的代码确实通过左值表达式 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 的系统上,您将很可能看到两次打印相同的值(假设第一次没有出现段错误)。但是,这不是您应该期望或依赖的行为 - 这是一个编码错误,句号。
假设我有一段非常简单的代码(在此代码中 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 < n
的p[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 询问在二进制文件的特定调用中该值将始终相同吗? (这两段都阐明了这一点)
假设在未初始化的内存中有 它是未定义的阅读它的行为。 (我们不应该认为超越了程序的范围 - 在程序中我们会知道它的价值是什么 - 通过阅读它 - 而且它是 UB 来阅读它)。100
并且在一些操作之后如果你没有给它分配任何东西 - 那么值将是相同的。
但是没有用 - 即使那样。你不能使用它。因为使用它是未定义的行为。
现在你怎么知道它们是一样的?你会考虑打印它。但是如果你打印它 - 那么它将是一个未定义的行为。 读取未初始化的值是 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 将触发故障。
但是字符类型有一个例外,即 char
、signed char
和 unsigned char
。这些类型 不能 具有陷阱表示。
来自 C standard 关于类型的第 6.2.5 节:
15 The three types
char
,signed char
, andunsigned char
are collectively called the character types. The implementation shall definechar
to have the same range, representation, and behavior as eithersigned char
orunsigned 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
问题在于陷阱表示的可能性。具体来说:
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.
您的代码确实通过左值表达式 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 的系统上,您将很可能看到两次打印相同的值(假设第一次没有出现段错误)。但是,这不是您应该期望或依赖的行为 - 这是一个编码错误,句号。