读取未初始化的整数时的行为无法预测

时间:2018-09-27 18:11:50

标签: c

有人可以向我解释为什么*x始终返回0*(x + 1)返回诸如-805306368536870912之类的值吗?

int * x = malloc(sizeof(int) * 2);
printf("%d, %d\n", *x, *(x + 1));

我正在使用gcc,但是在clang中也遇到了相同的行为。

我的理解是malloc将在堆上为两个int值分配足够的内存。我假设*x将引用第一个未初始化的int,而*(x + 1)将引用第二个未初始化的int

我不认为这是this的副本,因为*x总是 0。我对*(x + 1)为什么返回“垃圾”有一个不错的了解,但是对于*x如此0如此稳定的原因却知之甚少。

2 个答案:

答案 0 :(得分:2)

*x等效于*(x + 0)*(x + n)等效于x[n],其中x是指针,n是整数。因此,您要打印x[0]x[1]-整数数组的第一和第二个元素。


malloc分配的对象的字节是不确定的,除非已初始化,因此对象的值也是如此。该标准说(C11 3.19.2-3.9.4; same text in C17 but cannot link it as nicely)

  

确定价值

     

未指定值或陷阱表示

  

未指定值

     

在任何情况下本国际标准均不要求选择哪种值的相关类型的有效值

     

注意。未指定的值不能是陷阱表示。

  

陷阱表示

     

不需要表示对象类型值的对象表示形式

int对象在GCC中不能包含陷阱表示。但是,该行为仍然没有得到很好的说明,因为该标准并未对在任何实例中选择哪个值强加要求-甚至

printf("%d, %d\n", x[0], x[0]);

可以打印

0, 42

因此“检查”不确定值是没有意义的。

“假设规则”允许编译器完全取消对malloc的调用-这样,即使malloc实现始终将前4个字节清零的块,也可以将编译后的代码x[0]的号码有任何变化。

答案 1 :(得分:0)

  

我假设* x将引用第一个未初始化的int,而*(x + 1)将引用第二个未初始化的int。

您的假设是正确的。但是正如您自己说的那样:您正在取消引用具有不确定值的未初始​​化数据的指针。但是,仅从数据来看,就无法确定其是否已初始化。

要初始化内存,请使用calloc()

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

int main(void)
{
    int *x = calloc(2, sizeof *x);
    printf("%d, %d\n", x[0], x[1]);
}
  

我最终希望使用一种方法来遍历固定大小的数组,并仅显示已初始化的值。我不确定如何做到这一点。

使用第二个数组来跟踪分配:

#include <assert.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>

enum { LENGTH = 4 };

int main(void)
{

    int *data = calloc(LENGTH, sizeof *data);
    bool *initialized = calloc(LENGTH, sizeof *initialized);

    data[1] = 0;
    initialized[1] = true;

    data[3] = 42;
    initialized[3] = true;

    for (size_t i = 0; i < LENGTH; ++i)
        if (initialized[i])
            printf("%zu: %d\n", i + 1, data[i]);
}