我正在用未初始化的指针观察这种奇怪的行为。
从以下示例中可以看出,有时它会打印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)
答案 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)
具有自动持续时间的变量(即没有自变持续时间的局部变量) 静态存储类)开始包含垃圾,除非它们是 显式初始化。
答案 3 :(得分:0)
试试这个,而不是:
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
之前,已经发生在存储块中的随机信息位。因此行为未定义。