个人对指针的理解

背景:看《大话数据结构》的时候,发现作者使用指向指针的指针,而我在网上找到的例子使用指针,双方代码的效果一致。我不理解指向指针的指针有什么作用,在网上找到一些解释,看过代码的运行效果后,记录个人对指针的理解。

先来看令我困惑的情况,什么时候影响可以保留至原函数,什么时候影响无法保留至原函数?
以下代码中有一个主函数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语言函数传指针时究竟传的是什么?

C语言函数传指针时究竟传的是什么?_WarEric的博客-CSDN博客_指针做函数参数时传递的是什么

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值