一行: 在第一段代码中,您正在取消引用未初始化的指针,这会展示未定义的行为。在第二段代码中,您正在取消引用已初始化的指针,这将使您能够访问地址处的值。
有些解释:
首先,您需要意识到指针只是一个整数,而使用
*var
时,我们告诉编译器,我们将使用变量
var
(其中的整数)的内容作为地址来获取该地址中的值。如果有
**var
,同样地,我们告诉编译器,我们将首先使用存储在变量
var
中的值来获取地址上的值,然后再使用此获取的值作为地址,并获取存储在其中的值。
因此,在您的第一个声明中,它是:
+----------+ +----------+
| garbage | | garbage |
+----------+ +----------+
| a | | b |
+----------+ +----------+
| addr1 | | addr2 |
+----------+ +----------+
然后,您尝试使用存储在
a
中的值作为地址。
a
包含垃圾值,可以是任何值,但您无法访问任何地址位置。因此,当您执行
*a
时,它将使用
a
中存储的值作为地址。由于存储的值可以是任何值,因此任何事情都可能发生。
如果您有访问该位置的权限,则代码将继续执行而不会出现分段错误。如果地址恰好是堆结构或其他内存区域的地址,您的代码从堆栈中分配了该地址,则当您执行
*a = 10
时,它将简单地用
10
擦除该位置中的现有值。这可能会导致未定义的行为,因为现在您已经在没有实际授权的上下文中更改了某些内容。如果您没有对内存的访问权限,则只会收到分段错误。这称为未初始化指针的取消引用。
接下来的语句
a = &b
只是将
b
的地址分配给
a
。这并没有帮助,因为前一行已经取消引用了一个未初始化的指针。
在第三个语句后,下一个代码会有类似这样的内容:
+
| addr2 |
+
| a | +
+
| addr1 | | addr2 |
+
第三个语句将
b的地址赋给了
a。在此之前,
a没有被解引用,因此在初始化之前存储在
a中的垃圾值从未被用作地址。现在,当您将您所知道的有效地址分配给
a时,现在解引用
a将使您访问由
a指向的值。
扩展答案,您需要注意的是,即使您已将有效地址分配给指针,在解引用指针时,必须确保指针指向的地址的生命周期尚未过期。例如返回本地变量。
int foo (void)
{
int a = 50;
return &a;
}
int main (void)
{
int *v = foo ();
*v = 50;
return 0;
}
在访问堆中已释放的内存位置时同样如此。
int main (void)
{
char *a = malloc (1);
*a = 'A';
free (a);
*a = 'B';
return 0;
}