背景:看《大话数据结构》的时候,发现作者使用指向指针的指针,而我在网上找到的例子使用指针,双方代码的效果一致。我不理解指向指针的指针有什么作用,在网上找到一些解释,看过代码的运行效果后,记录个人对指针的理解。
先来看令我困惑的情况,什么时候影响可以保留至原函数,什么时候影响无法保留至原函数?
以下代码中有一个主函数main(),其中有2个普通变量、2个指针和1个指向指针的指针,另有两个子函数。一个是change_ptr(),形参是两个指针,在该函数中将一个指针里的内容交给另一个指针;另一个是change_ptr_ptr(),形参是一个指针和一个指向指针的指针,在函数中将指针里的内容交给指向指针的指针。
#include<iostream>
using namespace std;
void change_ptr(int* x, int* y);
void change_ptr_ptr(int** x, int* y);
int main()
{
int a, b;
a = 10;
b = 20;
int* d, * e, ** f;
d = &a;
e = &b;
f = &e;
cout << endl;
change_ptr(e, d);
cout << "after using two pointers to change\n";
cout << "name" << '\t' << "address" << '\t' << "content" << endl;
cout << "ptr_d" << '\t' << &d << '\t' << d << '\t' << *d << endl;
cout << "ptr_e" << '\t' << &e << '\t' << e << '\t' << *e << endl;
cout << endl;
change_ptr_ptr(f, d);
cout << "after using a pointer to pointer to change\n";
cout << "name" << '\t' << "address" << '\t' << "content" << endl;
cout << "ptr_d" << '\t' << &d << '\t' << d << '\t' << *d << endl;
cout << "ptr_e" << '\t' << &e << '\t' << e << '\t' << *e << endl;
return 0;
}
void change_ptr(int* x, int* y)
{
x = y;
}
void change_ptr_ptr(int** x, int* y)
{
*x = y;
}
运行结果如下:
after using two pointers to change
name address content
ptr_d 00BCF808 00BCF820 10
ptr_e 00BCF7FC 00BCF814 20
after using a pointer to pointer to change
name address content
ptr_d 00BCF808 00BCF820 10
ptr_e 00BCF7FC 00BCF820 10
ptr_ptr_f 00BCF7F0 00BCF7FC 00BCF820 10
可以发现,主函数中无法体现子函数change_ptr()的运行效果,但是可以体现子函数change_ptr_ptr()的运行效果。检查各变量在不同时期的地址和内容:
name address content
a 00BCF820 10
b 00BCF814 20
ptr_d 00BCF808 00BCF820 10
ptr_e 00BCF7FC 00BCF814 20
ptr_ptr_f 00BCF7F0 00BCF7FC 00BCF814 20
name address content
ptr_x 00BCF710 00BCF820 10
ptr_y 00BCF714 00BCF820 10
after using two pointers to change
name address content
ptr_d 00BCF808 00BCF820 10
ptr_e 00BCF7FC 00BCF814 20
name address content
ptr_y 00BCF714 00BCF820 10
ptr_ptr_x 00BCF710 00BCF7FC 00BCF814 20
after using a pointer to pointer to change
name address content
ptr_d 00BCF808 00BCF820 10
ptr_e 00BCF7FC 00BCF820 10
ptr_ptr_f 00BCF7F0 00BCF7FC 00BCF820 10
检查地址和内容的代码如下:
#include<iostream>
using namespace std;
void change_ptr(int* x, int* y);
void change_ptr_ptr(int** x, int* y);
int main()
{
int a, b;
a = 10;
b = 20;
int* d, * e, ** f;
d = &a;
e = &b;
f = &e;
cout << "name" << '\t' << "address" << '\t' << "content" << endl;
cout << "a" << '\t' << &a << '\t' << a << endl;
cout << "b" << '\t' << &b << '\t' << b << endl;
cout << "ptr_d" << '\t' << &d << '\t' << d << '\t' << *d << endl;
cout << "ptr_e" << '\t' << &e << '\t' << e << '\t' << *e << endl;
cout << "ptr_ptr_f" << '\t' << &f << '\t' << f << '\t' << *f << '\t' << **f << endl;
cout << endl;
change_ptr(e, d);
cout << "after using two pointers to change\n";
cout << "name" << '\t' << "address" << '\t' << "content" << endl;
cout << "ptr_d" << '\t' << &d << '\t' << d << '\t' << *d << endl;
cout << "ptr_e" << '\t' << &e << '\t' << e << '\t' << *e << endl;
cout << endl;
change_ptr_ptr(f, d);
cout << "after using a pointer to pointer to change\n";
cout << "name" << '\t' << "address" << '\t' << "content" << endl;
cout << "ptr_d" << '\t' << &d << '\t' << d << '\t' << *d << endl;
cout << "ptr_e" << '\t' << &e << '\t' << e << '\t' << *e << endl;
cout << "ptr_ptr_f" << '\t' << &f << '\t' << f << '\t' << *f << '\t' << **f << endl;
return 0;
}
void change_ptr(int* x, int* y)
{
x = y;
cout << "name" << '\t' << "address" << '\t' << "content" << endl;
cout << "ptr_x" << '\t' << &x << '\t' << x << '\t' << *x << endl;
cout << "ptr_y" << '\t' << &y << '\t' << y << '\t' << *y<< endl;
cout << endl;
}
void change_ptr_ptr(int** x, int* y)
{
cout << "name" << '\t' << "address" << '\t' << "content" << endl;
cout << "ptr_y" << '\t' << &y << '\t' << y << '\t' << *y << endl;
cout << "ptr_ptr_x" << '\t' << &x << '\t' << x << '\t' << *x << '\t' << **x << endl;
*x = y;
cout << endl;
}
通过检查地址和内容可以函数间的传递过程,
在子函数change_ptr()中,创建局部指针x和局部指针y,来接收指针d和e里面的内容,即普通变量b和a的地址,在子函数终止后局部指针x和y被释放。所以指针x和y对主函数的影响局限于:查找、修改主函数里的普通变量a和b的内容。将普通变量a的地址交给指针x,不影响主函数中指针d和e里面的内容。
在子函数change_ptr_ptr()中,创建局部指针y和指向指针的指针x,来接收指针d和指向指针e的指针f里面的内容,即普通变量a的地址和指针e的地址,在子函数终止后局部指针y和指向指针的指针x被释放。所以在子函数中,可以对主函数指针e里面的内容进行改动,于是将普通变量a的地址放入指针e这一行为的效果得以持续至主函数。相似的,在子函数中通过指向指针的指针x来调取指针e里面的内容,交给局部指针y,不会影响主函数里的指针d。
我还通过取内容符 * 和取地址符 & 之间的区别,发现另一些此前未注意到的内容:
创建指针,指针所在的地址和普通变量一样,是自动分配的,但是指针不能直接指向值,所以要么将普通变量的地址交给指针,要么使用new或者malloc动态分配一个地址,指针存储这个地址,这个地址存储值。
将普通变量的地址交给指针,则可以通过指针修改普通变量,直接修改普通变量后,指针也可以看到更新情况。
使用new或者malloc动态分配一个地址,指针存储这个地址,这个地址存储值,则无法通过指针修改普通变量,普通变量发生修改后,也无法通过指针看到更新情况。
以下是说明代码
int main()
{
int a, b;
a = 10;
b = 20;
int* p;
//*p = a;//报错 C6001 使用未初始化的内存"p"
p = &a;
int* q = &a;//要想在初始化的时候赋值,只能给地址
int* r;
r = new int;
*r = b;
cout << "p里面的内容是 " << *p << endl;
a = 60;
cout << "修改a=60之后,p里面的内容是 " << *p << endl;
cout << "r里面的内容是 " << *r << endl;
b = 30;
cout << "修改b=30之后,r里面的内容是 " << *r << endl;
delete r;
return 0;
}
运行结果如下
p里面的内容是 10
修改a=60之后,p里面的内容是 60
r里面的内容是 20
修改b=30之后,r里面的内容是 20
总结:函数之间传递的都是变量包含的内容,如果传递普通变量,则接收函数创建一个局部变量来存储原函数变量的值,该局部变量对值进行的操作不会影响原函数内的变量。如果传递指针,则接收函数创建一个局部指针来存储原函数指针中存储的地址,该局部指针对这个地址里的值的操作会影响到原函数变量的值。可以概括为上级对下级的改动可以保存,同级之间的改动不会保存。
参考材料:
1. 指向指针的指针有什么作用?
c - Why use double indirection? or Why use pointers to pointers? - Stack Overflow
2. C语言函数传指针时究竟传的是什么?