未初始化指针的奇怪行为
我用未初始化的指针观察这种奇怪的行为。
从下面的示例中可以看出,有时它会打印一个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)
通常, 根本不要使用未初始化的变量 。 如果您想了解更多信息,请继续阅读。
由于此标准段落,您的所有示例都是直接未定义行为(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 +修正案又名C11。
根据C99标准(n1256):
6.7.8初始化
…
10如果未显式初始化具有自动存储持续时间的对象,则其值不确定。
具有自动持续时间的变量(即没有静态存储类的局部变量)开始包含垃圾,除非它们被显式初始化。
资料来源: http : //c-faq.com/decl/initval.html
试试这个,改为:
int *i = 0; printf("%p\n", i); int *j = 0; printf("%p\n", j);
如果您愿意,可以写= NULL
而不是= 0
,但不管怎样,区别在于指针是初始化的。
为什么这很重要? 答案:因为i
和j
是变量。 也就是说, i
和j
是计算机内存区域中称为堆栈的小型保留存储块。 根据像int *i
这样的指针定义,这种存储块的目的是保存一个地址。 但是,在您的情况下,您没有为存储块写入任何地址; 所以,当你的printf
试图读取地址时,它只是得到了什么,在定义int *i
之前已经发生在存储块中的随机信息位。 因此行为未定义。