自以为对指针这东西已经了解得差不多,结果前几天还是掉进了坑里。算了算了,趁着刚从坑里爬出来,还是先记录一下好了。
一、指针
一切都是从指针开始的。指针的本质是一个内存地址,表示一个值在内存中的首地址,而长度则是由类型本身决定的。声明一个指针之后必须将其赋值给一个确定的地址,用完一个指针之后必须将其设为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 &lt;&lt; a &lt;&lt; std::endl; //可能的输出:00D0EAC8 delete_ptr(a); std::cout &lt;&lt; a &lt;&lt; std::endl; //可能的输出:00D0EAC8 return 0; }
……这根本什么都没删掉嘛。但是如果修改一下,传入指针的指针:
void delete_ptr(int** a) { delete *a; *a = nullptr; } int main() { int *a = new int(10); std::cout &lt;&lt; a &lt;&lt; std::endl; //可能的输出:00D0EAC8 delete_ptr(&amp;a); std::cout &lt;&lt; a &lt;&lt; std::endl; //输出:00000000 return 0; }
你看,这就正常了。当然,如果是C++的话也可以用传指针的引用代替指针的指针,用起来还舒服一点:
void delete_ptr(int*&amp; a) { delete a; a = nullptr; } int main() { int *a = new int(10); std::cout &lt;&lt; a &lt;&lt; std::endl; //可能的输出:00D0EAC8 delete_ptr(a); std::cout &lt;&lt; a &lt;&lt; std::endl; //输出:00000000 return 0; }
当然,如果拓展一下思路,很容易想到数组会自动退化成指针,那么传入一个“指针的指针”也就可以传入一个数组的指针。事实上,不知道是否有人注意过main函数的两个参数中的“argv”,其类型就是“char*”或者“char[]”,也是这个道理。