为什么这个print语句改变了前一个指针?

时间:2013-03-12 16:20:59

标签: c pointers printf

这是程序:

#include <stdio.h>

main() {
  int * i;
  int * j;
  printf("%d\n", i);
  printf("%d\n", j);
}

我编译并运行它,输出是:

888086464
0

但是,如果我注释掉第二个printf

#include <stdio.h>

main() {
  int * i;
  int * j;
  printf("%d\n", i);
  //printf("%d\n", j);
}

out out beomes:

0

我想知道为什么第二个printf改变了指针i 并且,C如何初始化指针?据我所知,如果指针未初始化,它的值Null将等于0,对吗?为什么在第一个输出中,i被初始化了?

编辑:根据你们的建议,我做了一些改变:

#include <stdio.h>

main() {
  int * i;
  int * j;
  printf("%p\n", i);
  printf("%p\n", j);
}

给出了

0x7fff67a361b0
(nil)

但是当我注释掉第二个printf

#include <stdio.h>

main() {
  int * i;
  int * j;
  printf("%p\n", i);
  //printf("%p\n", j);
}

输出为零。 我很好奇为什么在第一个版本中有两个printf,只有j是nil而我不是。

8 个答案:

答案 0 :(得分:4)

您没有初始化任何一个指针。你在看什么是未定义的行为。您看到的值只是堆栈中的垃圾值,每次运行时都会更改。

答案 1 :(得分:3)

如果你没有初始化指针,它可以有任何值。

必须初始化它们。否则,您将获得undefined behavior

指针打印的正确方法是:

printf("%p", pointer);

答案 2 :(得分:2)

您可以通过查看两个版本的生成机器代码来回答您的问题(对于gcc,应该是-S选项)。

怀疑在第二个版本中,根本没有创建j(因为它没有在任何地方使用),因此正在创建i在用于j的插槽中。无论出于何种原因,您的堆栈已设置为前64位为0x0000000000000000,后跟0x00007fff67a361b0

IOW,在第一个版本中,您的堆栈看起来像

 Item        Address            00  01  02  03  04  05  06  07   
 ----        -------            --  --  --  --  --  --  --  --
    j        0x8000             00  00  00  00  00  00  00  00
    i        0x8008             00  00  7f  ff  67  a3  61  b0

而在第二个版本中,它看起来像

 Item        Address            00  01  02  03  04  05  06  07   
 ----        -------            --  --  --  --  --  --  --  --
    i        0x8000             00  00  00  00  00  00  00  00
             0x8008             ??  ??  ??  ??  ??  ??  ??  ??

(地址值仅用于说明,并不代表任何真实的架构。)

对于咯咯笑声,请将print语句修改为

printf("%p: %p\n", &i, i);
printf("%p: %p\n", &j, j);
第一个版本中的

printf("%p: %p\n", &i, i);

在第二个。我敢打赌,第二版中为&i打印的值(变量i的地址)将与为&j打印的值相同(变量的地址) j)在第一个版本中。

注意 - 这与C 语言无关,而且与您的特定实现(编译器,链接器等)有关。我不相信你正在调用未定义的行为(你没有通过这些无效的指针访问任何内存),但你看到的是没有显式初始化指针值的危险。

在没有static关键字的块范围内声明的变量未初始化;在实例化变量时内存中的任何内容都是初始值,并且该位模式可能不代表该类型的有效值(这称为陷阱表示)。在文件范围(在任何函数之外)或使用static关键字声明的变量初始化为0或NULL,具体取决于它是标量还是指针类型。对于聚合类型(数组,结构和联合),规则稍微复杂一些,但基本原理是相同的。

答案 3 :(得分:0)

“未初始化”与“初始化为0”(或NULL)非常不同。

变量ij未初始化。它们留下了记忆中的内容。

当你注释掉第二个printf时,变量j不再使用了。它可能由编译器优化(意思是根本没有定义)。在这种情况下,i可能位于不同的位置,具有不同的未初始化的值。

这实际上是未定义的行为。我的上一节是纯粹的推测,你永远无法确定。

答案 4 :(得分:0)

指针可以容纳任何东西。除非你告诉它,否则C不会初始化本地(自动)变量。如果将它们定义为静态,则使用NULL初始化它们。

此外,您应该使用%p来打印指针,而不是%d

答案 5 :(得分:0)

如果不初始化指针地址

,您将获得未定义的行为

这里你将指针地址打印为int,这是不正确的,你应该使用"%p"而不是"%d"

答案 6 :(得分:0)

And, how does C initialize pointers?

C没有,你这样做。

As far as I know, if a pointer is not initialized, it would have value Null which is equal to 0, correct?

错误。如果未初始化,则没有定义的值。这将是垃圾。打印未初始化的变量将导致垃圾,这就是您无法预测输出的原因。

Why in the first output, the i was initialized?

事实并非如此,它恰好指向了0。

附注:当您打印指针时,应使用%p进行打印。

答案 7 :(得分:0)

C仅自动初始化static和全局变量(全局变量在函数外声明)为零。 自动变量(即你的两个指针被调用的内容)不会自动初始化,因此您正在打印&#34;堆栈噪音&#34; (即,当你的函数初始化时,堆栈上的任何值都会发生。打印的值甚至可能会在程序的运行之间发生变化。如果你想将它们初始化为NULL,你需要告诉编译器:

int *i = NULL;
int *j = NULL;