一行:第一个代码您取消引用未初始化的指针,该指针表现出未定义的行为,在第二个代码中您取消引用初始化的指针,该指针将访问地址处的值。
一点解释:
首先你需要意识到指针只不过是一个整数,通过*var我们告诉编译器我们将使用变量var(其中的整数)的内容作为地址获取该地址中的值。如果有**var,类似地我们告诉编译器我们将首先使用变量var的存储值来获取地址处的值,然后再次使用这个获取的值作为地址并获取存储在其中的值。
因此在您的第一个声明中是:
+----------+ +----------+
| garbage | | garbage |
+----------+ +----------+
| a | | b |
+----------+ +----------+
| addr1 | | addr2 |
+----------+ +----------+
然后您尝试使用存储在a 中的值作为地址。 a 包含垃圾,它可以是任何值,但您无权访问任何地址位置。因此,下一次您执行*a 时,它将使用a 中存储的值作为地址。因为存储的值可以是任何东西,所以任何事情都可能发生。
如果您有权访问该位置,则代码将继续执行而不会出现分段错误。如果地址恰好是堆簿记结构中的地址,或者您的代码从堆或堆栈中分配的其他内存区域,那么当您执行*a = 10 时,它将简单地用10 擦除该位置的现有值.这可能会导致未定义的行为,因为现在您在不知道上下文的情况下更改了具有实际内存权限的内容。如果您没有访问内存的权限,您只会遇到分段错误。这称为取消引用未初始化的指针。
下一个语句a = &b 只是在a 中分配b 的地址。这无济于事,因为上一行取消了对未初始化指针的引用。
接下来的代码你在第三条语句之后有这样的东西:
+----------+ +----------+
| addr2 |---+ | garbage |
+----------+ | +----------+
| a | +--> | b |
+----------+ +----------+
| addr1 | | addr2 |
+----------+ +----------+
第三条语句将b的地址分配给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;
}