C++指针专题(基础扫盲)

指针的学习是每个C/C++初学者都无可避免的,最近在学习C++的同时,顺便复习了一下以前学的C的指针,有些地方本来不是很明白的现在也豁然开朗了(⊙o⊙)

什么是指针

让我们看看维基百科上的定义:计算机中的内存都是编址的,每个地址都有一个符号,就像家庭地址或者IP地址一样。指针,是一个无符号整数(unsigned int),它是一个以当前系统寻址范围为取值范围的整数。声明指针和声明一个无符号整数实质上并无区别。
现在,我们从四个方面来考虑这句话:

1.指针的地址

首先我们回忆一下我们是创建定义程序中的变量的:

int a;//整型变量
char ch;//字符型变量

当我们创建完成后,内存就会为这些变量分配一个取决于变量大小的空间,这些变量就存储在这些空间上,且每个空间都有独一无二的地址,就像自家门牌号一样。同理,我们在程序中定义一个指针变量方式也是一样的:

int *p;
char *pchar;

这里的符号 * 是声明指针特有的,用来表示这个变量它是一个”指针“。那么,既然是我们自己创建的变量,那它也同样有资格在内存上占有一席之地。因此,每个指针变量也有自己的家门牌号。


2.指针的值

重点来了,指针之所以为指针,正是因为它的值与众不同。回忆以前学习过的变量类型:int,char,int[ ],double,float等等,它们的值是一个确切的值,而指针的值,是一串地址,这个地址是内存空间上已有的,属于另一个变量的地址。说白了,就是别人家的门牌号,逃)因此,我们也可以说指针”指向“了另一个变量。


3.指针初始化

当一个指针变量被创建时,它并没有指向某一变量,或者说没有指向我们所“期望”的变量。为什么这么说?因为我们知道所有未初始化的变量它的输出是一个很大的”垃圾数“,而这个”垃圾数“很可能凑巧就是某一变量的地址,所以未初始化的指针可能就指向了该变量,而我们对此却是无从而知的。因此,我们需要养成良好的习惯,即创建指针的同时为其初始化,以免发生内存安全问题。
指针初始化可如下:

int *p=NULL;
int a;
int *q=&a;

4.指针类型

指针的变量必须是和指向的变量保持一致性。比如指向int型变量,那么指针类型就必须是int *,其他类型同理。


其他类型指针

数组指针

运算符优先级:
()>[ ]> *
首先我们应先明确,我们以前创建的数组如int a[10];,其名字a就是一个指针常量。
顾名思义,数组指针就是一个指向数组的指针,其创建语法如下:

int a[3]={1,2,3};
int (*p)[3]=&a;

上述代码中,我们创建了一个int( *)[ ]类型的指针,那么理所当然,它指向的变量类型一定是int [ ],只要去掉 * 即可。我们将数组a的地址传递给指针p,此时p就指向了该数组。
此后,我们可以对该指针进行操作:

printf("%p\n",p);//数组a的地址
printf("%p\n",&a);//数组a的地址
printf("%p\n", a);//数组a的首元素地址
printf("%p\n",*p);//数组a的首元素地址

以上四个输出都是一样的,都是数组a的地址。
若想得到a[1]的值,只需执行以下语句:

printf("%d\n",(*p)[1]);

因为在数组指针中,(*p)等价于a


指针数组

顾名思义,指针数组本身就是一个我们熟悉的数组,只不过其数组元素全是指针。

int* p[2];//包含两个整型指针的数组
int a=10,b=20;
p[0]=&a;
p[1]=&b;
printf("%d  %d\n",*p[0],*p[1]);//输出为10,20

以上代码中,我们声明了 int * 类型的数组,其每个元素的类型都是int * ,分别指向整型变量a和b。


二级指针

二级指针就是指针的指针,它所指向的变量是另一个指针,所以二级指针的值存储的是另一指针的地址,而另一指针存储的是它所指向的变量的地址。

int a=5;
int* p=&a;
int** pp=&a;
printf("%d  %d\n",*p,**p);//答案都为5

看看这个图可能会明白一点。
指针描述

二级指针番外·

字符串指针数组篇

在C语言中,我们通过创建一个字符数组来存储一个以 ’\0’ 结尾的字符串,同样我们也可以用指针来创建一个字符串,并且对它进行赋值:

	const char *ps = "hello world";
	char s[20] = "hello world";
	printf("%s %s\n", ps, s);//结果都为hello world
	ps = "good bye";//正确
	s = "good bye";//错误,无法编译

为什么字符指针 ps 可以直接用 = 号赋值而数组 s 不可以呢?还是那句话,指针存储的是一个地址,在这里指针ps存储的就是字符串 ”hello world“ 的首字符’ h’ 的地址,在后面把 "good bye"赋值给ps的时候,“hello world” 字符串仍然在内存空间中存在,只不过是指针 ps 改变了存储的地址,指向了字符串 “good bye”。

	const char * ps = "hello world";
	printf("%p\n", ps);//在我的PC上是0x00E19B98
	ps = "good bye";
	printf("%p\n", ps);//在我的PC上是0x00E19E08,和上面不同

言归正传,开始创建一个字符串指针数组并初始化。

	const char * ps[2] = { "hello world","good bye" };
	printf("%s\n", ps[0]);//ps[0]存储的是第一个字符串的首字符'h'的地址,其输出是整个字符串	
	printf("%c\n", *(ps[0]+1));//对'h'的地址解除引用那么就是'h'这个字符

然后,我们用一个二级指针来指向这个字符串指针数组。

	const char **pstr = ps;
	printf("%c\n", **pstr);//输出为'h'
	pstr++;
	printf("%c\n", *(*pstr+3));//输出为第二个字符串中的'd'

解释一下上述代码结果:

  1. 将 * * pstr分解成两步:第一步 * 和 pstr 结合,指针解除一级引用,这时,* pstr是指向第一个字符串首地址的指针。
    第二步:* pstr 再和 * 结合,解除二级引用,此时的 * * pstr为第一个字符串首地址的解引用,即为 ‘h’ 。
  2. pstr++表示 指针 pstr 指向了 字符串指针数组的第二个元素:一个指向 “good bye” 的指针。
  3. 将*(* pstr+3)分解成三步:第一步 * 和 pstr 结合,指针解除一级引用,这时 * pstr 是指向第二个字符串第一个字符地址的指针。
    第二步: * pstr +3 ,即将指针 * pstr在第二个字符串上的位置向后移动三个单位,此时,* pstr 是指向第二个字符串第四个字符地址的指针。
    第三步 : * (*pstr +3),就是将指针( * pstr +3)解除引用,原来它指向的是字符 ’d’ 的地址,那么解除引用后就是字符 ‘d’ 。
    可以参考以下图片:
    字符串指针数组
    如果还有疑惑,没关系,再举一个例子:
    int a = 10, b = 20;
	int *p[2] = { &a,&b };//创建了一个整型指针数组,用a和b的地址来初始化
	int **pp = p;//创建了一个指向整型指针数组的二级指针
	printf("%d %d\n", *p[0], **pp);//输出都是10
	printf("%d %d\n", *p[1], *(*(pp + 1)));//输出都是20

过程可参考以下图片整型指针数组
有问题欢迎私信我,转载请注明出处,谢谢~

  • 90
    点赞
  • 323
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值