一行:第一个代码您正在取消引用未初始化的指针,该指针表现出未定义的行为,在第二个代码中,您正在取消引用已初始化的指针,该指针将访问该地址处的值。
一点解释:
首先,您需要意识到指针只不过是一个整数,然后*var
我们告诉编译器我们将使用变量的内容var
(其中的整数)作为地址来获取该地址中的值。如果有**var
类似的情况,我们告诉编译器我们将首先使用变量的存储值var
来获取地址处的值,然后再次使用这个获取的值作为地址并获取存储在其中的值。
因此,在您的第一个声明中,它是:
+----------+ +----------+
| garbage | | garbage |
+----------+ +----------+
| a | | b |
+----------+ +----------+
| addr1 | | addr2 |
+----------+ +----------+
然后您尝试将存储的值a
用作地址。a
包含垃圾,它可以是任何值,但您无权访问任何地址位置。因此,下一刻您*a
将使用存储的值a
作为地址。因为存储的值可以是任何东西,所以任何事情都可能发生。
如果您有权访问该位置,则代码将继续执行而不会出现分段错误。如果该地址恰好是堆簿记结构中的地址,或者是您的代码从堆或堆栈中分配的其他内存区域,那么当您这样做*a = 10
时,它将简单地擦除10
该位置中的现有值。这可能会导致未定义的行为,因为现在您在不知道上下文的情况下更改了具有实际内存权限的内容。如果您没有访问内存的权限,您只会遇到分段错误。这称为取消引用未初始化的指针。
您执行的下一条语句a = &b
仅分配b
in的地址a
。这无济于事,因为前一行已取消引用未初始化的指针。
下一个代码在第三条语句之后你有这样的东西:
+----------+ +----------+
| addr2 |---+ | garbage |
+----------+ | +----------+
| a | +--> | b |
+----------+ +----------+
| addr1 | | addr2 |
+----------+ +----------+
第三条语句分配b
into的地址a
。在此之前a
不会取消引用,因此在a
初始化之前存储的垃圾值永远不会用作地址。现在,当您将知识的有效地址分配给 时a
,现在取消引用a
将使您能够访问 所指向的值a
。
扩展答案,您需要注意,即使您已为指针分配了有效地址,您也必须确保在取消引用指针时指针指向的地址的生命周期没有过期。例如返回局部变量。
int foo (void)
{
int a = 50;
return &a; //Address is valid
}//After this `a' is destroyed (lifetime finishes), accessing this address
//results in undefined behaviour
int main (void)
{
int *v = foo ();
*v = 50; //Incorrect, contents of `v' has expired lifetime.
return 0;
}
在从堆访问释放的内存位置的情况下也是如此。
int main (void)
{
char *a = malloc (1);
*a = 'A'; //Works fine, because we have allocated memory
free (a); //Freeing allocated memory
*a = 'B'; //Undefined behaviour, we have already freed
//memory, it's not for us now.
return 0;
}