未初始化指针的奇怪行为

时间:2014-09-11 15:18:39

标签: c pointers

我正在用未初始化的指针观察这种奇怪的行为。

从以下示例中可以看出,有时它会打印NULL值,而其他人会以交替方式打印有效地址。

为什么会这样?

代码

int *i;
printf("%p\n", i);

输出
(无)

<小时/>

代码

int *i;
printf("%p\n", i);

int *j;
printf("%p\n", j);

输出
0x7fff2d0c1b50
(无)

<小时/>

代码

int *i;
printf("%p\n", i);

int *j;
printf("%p\n", j);

int *k;
printf("%p\n", k);

输出
(无)
0x7fffda5284b0
(无)

<小时/>

代码

int *i;
printf("%p\n", i);

int *j;
printf("%p\n", j);

int *k;
printf("%p\n", k);

int *l;
printf("%p\n", l);

输出
0x400510
(无)
0x7fff6d7089c0
(无)

<小时/>

代码

int *i;
printf("%p\n", i);

int *j;
printf("%p\n", j);

int *k;
printf("%p\n", k);

int *l;
printf("%p\n", l);

int *m;
printf("%p\n", m);

输出
0x357521cbc0
0x400520
(无)
0x7fff715849e0
(无)

<小时/>

系统:x86_64 x86_64 x86_64 GNU / Linux(x86_64-redhat-linux)
编译器:gcc版本4.1.2 20080704(Red Hat 4.1.2-52)

4 个答案:

答案 0 :(得分:5)

一般情况下,根本不使用未初始化的变量。如果您想了解更多信息,请继续阅读。

由于此标准段落,您的所有示例均为Undefined Behavior (UB)

  

6.3.2.1左值,数组和函数指示符

     

[...]
  2 [...]如果左值指定了一个自动存储持续时间的对象,该对象可能已使用register存储类声明(从未使用过其地址),并且该对象未初始化(未使用初始化程序声明)在使用之前没有对它进行任何分配),行为是未定义的。

现在,让我们在一些省略的行中假装地址。

&i; // Like this
  

6.2.4对象的存储持续时间

     

5一个对象,其标识符声明为没有链接且没有存储类   说明符static具有自动存储持续时间,一些复合文字也是如此。 [...]
  6 [...]对象的初始值是不确定的。

替代报价:

  

6.7.9初始化

     

[...]
  10如果未显式初始化具有自动存储持续时间的对象,则其值为   不确定的。

  

3.19.2

     

1不确定值
  要么是未指定的值,要么是陷阱表示

     

3.19.3

     

1未指定值
  本国际标准规定的相关类型的有效值   在任何情况下选择哪个值的要求
   2注意未指定的值不能是陷阱表示。

     

3.19.4

     

1个陷阱表示
  一个对象表示,不需要表示对象类型的值

  

6.2.6.1概述

     

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

因此,如果您的实现支持您所阅读类型的陷阱表示(您不想int*),则 UB

因为你没有,未指定的值适用,这意味着每次读取都返回一些任意值,而不是必须相同。


所有报价均来自草案n1570,C99 + Ammendments aka C11。

答案 1 :(得分:2)

来自C99标准(n1256):

  

6.7.8初始化

     

...

     

10如果没有显式初始化具有自动存储持续时间的对象,则其值为   不确定的。

答案 2 :(得分:1)

  

具有自动持续时间的变量(即没有自变持续时间的局部变量)   静态存储类)开始包含垃圾,除非它们是   显式初始化。

来源:http://c-faq.com/decl/initval.html

答案 3 :(得分:0)

试试这个,而不是:

int *i = 0;
printf("%p\n", i);

int *j = 0;
printf("%p\n", j);

如果您愿意,可以编写= NULL而不是= 0,但无论如何,区别在于指针已初始化。

为什么这很重要?答:因为ij是变量。也就是说,ij是计算机内存区域中的小型保留存储块,称为堆栈。此类块的目的是根据像int *i这样的指针定义,存储是保存一个地址。但是,在您的情况下,您没有为存储块写入任何地址;所以,当你的printf尝试读取地址时,它只会得到任何信息,在定义int *i之前,已经发生在存储块中的随机信息位。因此行为未定义。