「C++学习笔记」指针的理解

C++里,一直让我头疼的就是指针了,指针真的就是个p

所以花点时间研究了一下。

一、指针的“ * ”操作

指针的定义方式如下:

int *p = new int;

需要在名称前加上“*”,一直比较纠结的是,在使用的时候,什么情况情况下加“*”,什么情况下不加。

“*”就是取出指针所指向的东西。那么指针该如何赋值呢?

int *p = new int;
p = 10;
//以上会报错:不能将 int类型的值赋值给 int*类型的实体

有以上可以知道,在声明指针的时候,*p并不是指针,指针的本体是p,加上“*”即指针所指向的int型存储区域。

正确的赋值方式应该是:

*p = 10;

那么,不同指针有该如何赋值呢:

void main()
{
	int *p = new int;
	*p = 10;
	cout << "p=" << p << endl;
	int *q = new int;
	cout << "q=" << q << endl;
	q = p;
	cout << "q=" << q << endl;
	cout << "*q=" << *q << endl;
	cin.get();
}
/*-------------输出结果如下:
p=000001E22F8F5E50
q=000001E22F8F4C10
q=000001E22F8F5E50
*q=10
----------------------*/

声明指针p指向int型数据10,打印p,再声明指针q,并打印q,将p赋值给q,打印出q和*q。

从代码中看出,指针之间的赋值不用加“*”,指针之间的赋值,只是将等号右侧指针所存储的的地址,复制给等号左侧的指针变量。

总结:定义指针 *p,其中p是指针变量本体,理解为一个容器,这个容器里面是用来存放内存地址的,在使用该指针时,加上“*”,即是按照这个容器里的地址,去内存中找其对应的位置。

二、多级指针

在以上指针的理解上,开始研究多级指针,比如int *******************p(有点夸张了啊喂!)

不多说,先看代码:

void main()
{
	int ***ppp = new int**;    //声明三级指针
	int **pp = new int*;       //声明二级指针
	int *p = new int;          //声明一级指针
	*p = 300;        //一级指针指向int型数据
	*pp = p;         //二级指针指向一级
	*ppp = pp;       //三级指针指向二级

	cout << "***ppp=" << ***ppp << endl; 
	
	if (ppp != NULL){ delete(ppp); }
	if (pp != NULL) { delete(pp); }
	if (p != NULL) { delete(p); }
	cin.get();
}
/*-------------输出结果如下:
***ppp=300
----------------------*/

在上面的代码中,定义了一级、二级、三级指针,

既然已经说过“*”是按照zhi指针存储的地址找到对应的位置,那么是否可以进行如下操作:

int ***ppp = new int**;
int *p = new int;
*p = 300;
**ppp = p;

答案自然是不行的,这样写,不会有语法错误,也是可以编译通过的,但运行时就会抛异常。

指针ppp是三级指针,它只能指向二级指针变量。这里直接进行“**”这样的操作,逻辑上是取出了一级指针,但中间一级是没有定义的,不明确,所以在运行时,就会弹出指针越界的错误。

总结:可以把指针的“*”理解为一种“扒衣服”操作,有一个“*”即扒一层衣服;指针等赋值时,等号两边必须同等级。

即如:

int ***ppp = new int**;//ppp是三级指针
int **pp = new int*;//pp是二级指针
int *p = new int;//p是一级指针
*p = 300;//p加“*”即脱一层衣服,就与int型变量同等级,就可以进行赋值操作
*pp = p;//pp脱一层衣服,就是一级指针,就可以把一级指针p赋值给它
*ppp = pp;//ppp本身是三级指针,脱一层衣服就是二级,与pp同级
cout << "***ppp=" << ***ppp << endl;//给三级指针脱三层衣服,输出的结果就是指向的数据300

三、“ * ”与“ [ ] ”

当指针与数组同时出现的时候,那简直无比酸爽

先看一段代码:

int main()
{
	int a[3] = { 2,3,4 };
	int b[3] = { 8,7,6 };
	
	int **ab = new int*[2];
	ab[0] = a;
	ab[1] = b;
	//int *ab[2] = { pa,pb };//与上面的三行等效

	cout << "ab=" << ab << endl;
	cout << "&ab[0]=" << &(ab[0]) << endl;
	cout << "ab[0]=" << ab[0] << endl;
	cout << "&ab[1]=" << &(ab[1]) << endl;
	cout << "ab[1]=" << ab[1] << endl;
	cout << "*ab[0]=" << *ab[0] << endl;
	cout << "*ab[1]=" << *ab[1] << endl;
	cout << "(*ab)[1]=" << (*ab)[1] << endl;
	cout << "*(ab[1])=" << *(ab[1]) << endl;	
	cout << "ab[0][0]=" << ab[0][0] << endl;
	cout << "ab[0][1]=" << ab[0][1] << endl;
	cout << "ab[1][1]=" << ab[1][1] << endl;
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;

	cout << "a[1]=" << a[1] << endl;
	cout << "b[1]=" << b[1] << endl;
	return 0;
}

你能知道输出的结果各是什么吗?

----------------------揭晓答案:

ab        =000002249FB50220
&ab[0]    =000002249FB50220
ab[0]     =00000066E6AFF728
&ab[1]    =000002249FB50228
ab[1]     =00000066E6AFF758
*ab[0]      =2
*ab[1]      =8
(*ab)[1]    =3
*(ab[1])    =8
ab[0][0]    =2
ab[0][1]    =3
ab[1][1]    =7
a        =00000066E6AFF728
b        =00000066E6AFF758
a[1]        =3
b[1]        =7

1、ab是一个一维数组,数组元素均为指针,所以他是指向指针的二级指针。

输出ab,即ab这个一维数组的地址,也就是第一个元素的地址,也就是ab[0]的地址

但ab[0]是ab中的第一个元素,也是一个指针,直接输出得到的是这个一级指针存储的地址

所以:ab=&(ab[0]),

2、由于有ab[0] = a; 和ab[1] = b;,所以ab[0]和a是指向同一块内存的,所以这两个指针变量中存放的地址就相同

因此输出时ab[0] = a,ab[1] = b。

3、*ab[1]即先得到 ab[1]这个一级指针,这个指针变量中的地址与指针b中的地址一样,在“脱衣服”后,

*ab[1]等效于*(ab[1]),其指向的内容,同*b,也就是b[0],也就是数字8

4、(*ab)[1],时先得到*ab,也就是ab[0],再加上“[1]”,也就是ab[0][1];

其他的就不需要解释了,从上面的代码和运行结果,总结出一个很有意思的规律

就是:“ * ”和“ [ ] ”,有着相同的作用——给指针脱衣服,如果定义 int p[3]={1, 2, 3};则p[0]和*p是相同的

也就是说这里的的“p[x]”,就会使得p成为一个指针。

之前有总结,等号两别必须同级别,那么这里注意到一个问题,看如下两个指针的声明:

int *p = new int;
int *a = new int[3];

都是编译没有问题的,刚才发现规律时“*”和“ [ ] ”作用相同,那么可以理解int[x]是一级指针,可以和int *p画等号;

但第一行似乎不太对,

其实,第一行如果写成

int *p = new int[1]

这样以上的总结就都能说通了。

看一段复杂的代码:

int main()
{
	const char *str1[2] = { "abcdef", "123456" };
	const char *str2[2] = { "ABXDEF","zyxwvu" };
	const char ***pcharp = new const char**[2];
	pcharp[0] = str1;
	pcharp[1] = str2;
	
	cout << "str1=" << str1 << endl;
	cout << "str2=" << str2 << endl;
	cout << "str1[0]=" << str1[0] << endl;
	cout << "str2[1]=" << str1[1] << endl;
	cout << "*str1[0]=" << *str1[0] << endl;
	cout << "str2[0][2]=" << str1[0][2] << endl;
	cout << "pcharp=" << pcharp << endl;
	cout << "pcharp[0]=" << pcharp[0] << endl;
	cout << "*pcharp[0]=" << *pcharp[0] << endl;
	cout << "pcharp[0][1]=" << pcharp[0][1] << endl;
	cout << "*++pcharp=" << *++pcharp << endl;
	cout << "*++*pcharp=" << *++*pcharp << endl;
	cout << "***pcharp=" << ***pcharp << endl;
	cout << "pcharp[0][0]=" << pcharp[0][0] << endl;
	cout << "pcharp[0][0][0]=" << pcharp[0][0][0] << endl;

	cin.get();
	return 0;
}

以上代码,输出结果又是什么呢

按照前面总结的,我先给出一部分答案吧:

str1            =0000008E047BF838
str2            =0000008E047BF868
str1[0]         =abcdef
str2[1]         =123456
*str1[0]        =a
str2[0][2]      =c
pcharp          =00000192AEAB08C0
pcharp[0]       =0000008E047BF838
*pcharp[0]      =abcdef
pcharp[0][1]    =123456

ps:要注意,字符串本身就是char* 类型,char只能是单一字符

三、“*++p”和“p[1]”

指针是可以加减操作的,比如如下代码:

int main()
{
	int *p = new int[3];
	p[0] = 1; p[1] = 3; p[2] = 4;
	cout << "*p=" << *p << endl;
	cout << "*(p+1)=" << *(p+1) << endl;
	cout << "p[1]=" << p[1] << endl;
	cout << "p[+1]=" << p[+1] << endl;
	cout << "*++p=" << *++p << endl;
	cin.get();
	return 0;
}

其输出的结果是:

*p        =1
*(p+1)    =3
p[1]      =3
p[+1]     =3
*++p      =3

显然可以看出*(p+1) 、p[1] 、p[+1]  、*++p 是等价的。但真的是这样吗?将上面的代码中,数组声明定义改写为如下形式:

int p[3] = {1, 2, 3};

则此时程序是无法编译通过的,语法报错在“cout << "*++p=" << *++p << endl;”这一句,提示“++” “表达式必须是可修改的左值。

因为这种定义方式后,p虽然是指针,但不可以修改,它不能再指向其他内存,所以不能进行p=p+1的运算。

所以按照后一种定义方式,*(p+1) 、p[1] 、p[+1]  均能正常输出,且结果一致,但不能使用*++p。

如何理解可修改与不可修改呢,请注意下面两组代码:

const char* str = "123456";
cout << "*++str=" << *++str << endl;

str 虽然被const限制,但其指向谁依然是可修改的,只是指向的字符串不可修改,而下面的写法就会报错

const char str[7] = "123456";
cout << "*++str=" << *++str << endl;//str会报错,“必须是可修改的左值”

总结:“++”操作,只能对可修改的值进行操作,这里要正确理解“可修改的左值”

到这里还没结束,现在来考虑以下 在第二节中,没有输出完的的结果,请看如下:

int main()
{
	const char *str1[2] = { "abcdef", "123456" };
	const char *str2[2] = { "ABXDEF","zyxwvu" };
	const char ***pcharp = new const char**[2];
	pcharp[0] = str1;
	pcharp[1] = str2;
	
	cout << "*++pcharp=" << *++pcharp << endl;
	cout << "*++*pcharp=" << *++*pcharp << endl;
	cout << "***pcharp=" << ***pcharp << endl;
	cout << "pcharp[0][0]=" << pcharp[0][0] << endl;
	cout << "pcharp[0][0][0]=" << pcharp[0][0][0] << endl;

	cin.get();
	return 0;
}

 (我又把源代码插入了一次,有所缩减,方便查看)

str1            =0000008E047BF838
str2            =0000008E047BF868
str1[0]         =abcdef
str2[1]         =123456
*str1[0]        =a
str2[0][2]      =c
pcharp          =00000192AEAB08C0
pcharp[0]       =0000008E047BF838
*pcharp[0]      =abcdef
pcharp[0][1]    =123456
--------------------------------------
*++pcharp       =0000008E047BF868
*++*pcharp      =zyxwvu
***pcharp       =z
pcharp[0][0]    =zyxwvu
pcharp[0][0][0] =z

*pcharp应该是指针变量str1的地址,*++pcharp就应该是指针变量str2的地址,由结果看,显然是正确的

++*pcharp,应该是指向“123456”的指针变量的地址,加上“*”,*++*pcharp就应该指向“123456”,但输出的结果显然不对

***pcharp本应该是‘a’这里也成了‘z’。

这里就涉及到“++”的另一个坑,就是“++”运算的本质会覆盖变量原有的值

即: ++pcharp,其意义就是pcharp=pcharp+1,pcharp已经不是原来的指针了,它已经后移了。

总结:指针的“++”运算虽然很多情况下和“[+1]”能取得相同结果,但一定要注意“++”运算的本质,是会改变指针变量的!

先到这里,下一篇整理一下指针作为函数参数以及函数指针的东西吧。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值