关于指针的指针小结

自以为对指针这东西已经了解得差不多,结果前几天还是掉进了坑里。算了算了,趁着刚从坑里爬出来,还是先记录一下好了。

一、指针

一切都是从指针开始的。指针的本质是一个内存地址,表示一个值在内存中的首地址,而长度则是由类型本身决定的。声明一个指针之后必须将其赋值给一个确定的地址,用完一个指针之后必须将其设为NULL或nullptr(C++11起),否则会成为野指针。

举一个最为普通的例子:

 int *a;
 a = new int(10);
 std::cout << *a << std::endl;  //输出:10
 std::cout << a << std::endl;  //可能的输出:00D0EAC8
 delete a;
 a = nullptr;
 std::cout << a << std::endl;  //输出:00000000
 

↑以上都是老生常谈的内容,那么问题在哪呢?

问题在于“指针”到底是一个什么东西。

之前已经说过,指针的本质是一个内存地址,而这个“内存地址”的值也是需要一段内存去保存的,也就是说,一个指针类型的值也会有它自己的地址,这就是所谓“指针的指针”。那么,研究这个东西有什么用呢?或许没什么用,但是如果要用在函数中就很有用了。因为C语言不像C++那样可以引用传参(虽然引用的本质也是传指针),因此如果需要在一个函数中delete掉一个指针并置空,就需要传入“指针的指针”。

只传入普通的指针的例子如下:

void delete_ptr(int* a)
{
delete a;
a = nullptr;
}

int main()
{
int *a = new int(10);
std::cout << a << std::endl;  //可能的输出:00D0EAC8
delete_ptr(a);
std::cout << a << std::endl;  //可能的输出:00D0EAC8
return 0;
}

……这根本什么都没删掉嘛。但是如果修改一下,传入指针的指针:

void delete_ptr(int** a)
{
delete *a;
*a = nullptr;
}

int main()
{
int *a = new int(10);
std::cout << a << std::endl;  //可能的输出:00D0EAC8
delete_ptr(&a);
std::cout << a << std::endl;  //输出:00000000
return 0;
}

你看,这就正常了。当然,如果是C++的话也可以用传指针的引用代替指针的指针,用起来还舒服一点:

void delete_ptr(int*& a)
{
delete a;
a = nullptr;
}

int main()
{
int *a = new int(10);
std::cout << a << std::endl;  //可能的输出:00D0EAC8
delete_ptr(a);
std::cout << a << std::endl;  //输出:00000000
return 0;
}

当然,如果拓展一下思路,很容易想到数组会自动退化成指针,那么传入一个“指针的指针”也就可以传入一个数组的指针。事实上,不知道是否有人注意过main函数的两个参数中的“argv”,其类型就是“char*”或者“char[]”,也是这个道理。