假设我有一段非常简单的代码(在此代码中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
,p[i]
对所有0 <= i < n
一致的结果是什么?更具体地说,对于p[i]
的每次读取(在二进制文件的SAME调用中),对于等效的i
,结果是否相同?
换句话说,上述循环的给定迭代中的两个printf
语句是否会打印相同的值?
答案 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将触发故障。
但字符类型有例外,即char
,signed char
和unsigned char
。这些类型不能具有陷阱表示。
关于类型的C standard第6.2.5节:
15 这三种类型
char
,signed char
和unsigned char
统称为字符类型。实施应 将char
定义为具有相同的范围,表示和行为signed char
或unsigned 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要求在二进制文件的特定调用中该值始终相同? (两段都澄清了这一点)
假设未初始化的内存中存在它&#39; s阅读它的未定义行为。 (我们不应该认为该计划的范围是正确的 - 在计划中我们会知道它的价值 - 阅读它 - 以及它的UB来阅读它。)100
,并且在某些操作后仍然没有为其分配任何内容 - 那么值将是相同的。
但没有用 - 即使这样。你不能使用它。使用它是未定义的行为。
现在你怎么知道他们是一样的?你会考虑打印它。但是如果你打印它 - 那么它将是一个未定义的行为。 读取未初始化的值是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)。与此处的一些声明相反,读取不确定的值并不一定会产生不确定的行为。而且,一个不确定的值并不意味着它可以在程序运行时任意改变。它只是意味着值是
未指定的值或陷阱表示
问题在于陷阱表示的可能性。具体做法是:
某些对象表示不需要表示的值 对象类型。如果对象的存储值具有这样的值 表示,并由没有的左值表达式读取 字符类型,行为未定义。如果这样的表示是 由副作用产生,修改对象的全部或任何部分 通过一个没有字符类型的左值表达式, 行为未定义。这种表示称为陷阱 表示。
您的代码通过左值表达式p[i]
读取不确定的值。 如果 这些值中的任何一个恰好是陷阱表示,那么您的程序的行为是未定义的。否则,标准中没有任何理由证明(未指定的)值不被正确读取和重复。
现在就是这样的事情:从抽象的意义上说,你的程序有可能表现出未定义的行为。如果它确实表现出未定义的行为,那么所有的投注都会关闭,特别是,您分配的对象中的int
值可能会在没有警告的情况下发生变化,或者似乎也会这样做。因此,C确实无法保证您的程序将打印成对的相同数字。
另一方面,很少有C实现提供int
类型,可以提供任何陷阱表示,而您的类型不可能是一个。如果您的实现不提供类型int
的任何陷阱表示,那么分配对象的值的不确定性不会导致程序中的p[i]
表达式在该实现上运行时产生未定义的行为。在具有该共同特征的符合实现的情况下,您的程序将打印成对的重复值。