字符指针
常见的一种用法
首先大家有没有遇见过字符指针这样一种写法
这种写法是通过指针变量p来输出字符串,而当我们执行这样一个程序时会呈现出这样的结果
我们来深入了解一下这个代码
1.首先p作为一个char类型的指针变量只能存放一个char类型变量的地址。
2.其次“abcdef”作为一个字符串,我们可以把它当作一个简单的表示式,任何一个表达式都会有两种属性(值属性和类型属性),此时就会把首字符的地址当作其值属性,所以在后面的赋值操作时就会把‘a’的地址赋值给变量p。
3.最后通过printf()来得到打印字符串的效果:
能形成这样的结果主要还是因为printf()函数的作用
它在满足两个条件下:1.输出格式为%s
2.提供(该字符串的)起始地址
就会从起始位置的地址开始向后打印直到遇见'\0'
如果你还是心存质疑,我们再来看下面这个代码
这里分别输出p里面所存放的的地址和”abcdef“的地址(其实就是其中a的地址),两者是相同的关系
关于常量字符串的特殊“机制”
接下来我们简单认识一下常量字符串(不能被修改)和只读数据区(静态内存里面的一个区域,用于存放常量字符串,只能用来读,不能被修改里面的值)的概念
上面代码中的”abcdef“就是存放在常量字符区的常量字符串。(本篇不做过多解释)
当如果定义两个char类型的变量,并且同时指向相同的一个常量字符串,那么请思考这两个变量里面所存放的地址相同吗?
答案是相同
在这个只读数据区里面,由于里面的所有内容不能被修改,那么内存还有必要存两份相同的数据吗?所有像这种常量字符串,内存只会存一份。
再来看接下来的这段代码
在这里我们创建的是两个独立的数组,每个数组都被0,1,2,3初始化了本身,所以为了存放这些数组元素,arr1和arr2数组就会分别在内存中开辟出一块空间来存放各自的元素,每个数组都拥有自己独立的空间,所以这里的他们的地址是不同的。(其它类型的数组同理)
这里有本人画的一个简易图解
指针数组和数组指针
指针数组
在了解这种类型的数组之前,先来对之前我们学过的数组做一个拆解
int arr[6];我们可以将他拆成三个部分:数组元素类型(int),数组名(arr)和元素个数(6)。
其它类型的数组同理
怎么理解指针数组
指针数组顾名思义就是存放指针的数组,数组内的每个元素都是指针:
拿int* arr[6]这个指针数组举例:数组元素类型为(int*),数组名为(arr),元素个数为6。这就是一个存放整型指针的数组;同理char* arr1[6];就是存放字符指针的数组......
无意间形成的"二维数组"?
对于指针数组正常的用法我们就不过多解释了,直接来看不寻常的:
我们先来看下面这个代码
首先数组名正常情况下代表数组首元素的地址(有两种例外,如果不知道,百度就可以搜到),我们创建了一个用来存放整形指针的数组p,元素个数为3.
这里的arr1就代表arr1首元素的地址,也就是一个整形类型的指针,所以可以作为p数组里面的元素内存中就可以想象成这样
在这种情况下,就无意识的创建了一个与二维数组非常相似的一维数组
既然创建了这么一个”二维数组“,我们就来了解一下怎么使用它(这无非就是要掌握每个元素的表达方式)
对于p数组来说,p[0]代表的就是该数组的第一个元素也就是arr1的首元素的地址,p[0]+1就是arr1的第二个元素的地址,那么对它进行解引用操作*(p[0]+1),此时它的值如果用%d的形式打印出来就是2
当然并不只有这一种表现方法:在我们学习指针的时候,应该学过这样的用法 *(pa+i)等价于pa[i]
那么我们这里就可以把p[0]当作pa,就有了这种写法p[0][1];至此,是不是更加感觉p就是一个二维数组了,但其实他不是
数组指针
认识数组指针
我们很容易理解:有指向整形的指针,指向字符的指针等等。
那么数组指针就是指向数组的指针,而int (*p)[5]就是一个简单的数组指针
本人有一个如何判断的方法:对于int *p[5]和int(*p)[5]
int *p[5]:在这里p先与[5]结合,已经得出他是一个数组,int*代表每个元素的类型。所以是一个指针数组。
int(*p)[5]:p先与*结合,已经得出他是一个指针,int [5]代表改指针指向的对象(一个元素个数为5,类型为int的数组)。所以是一个数组指针。(如果你已经有了java数组的知识,那么这里会更容易理解)(这里的*不是解引用,而是为了告诉你p是一个指针)
另外有一点注意:[]里面必须写入数组元素,不能省略,否则系统会将它当作[0]
如何用数组指针遍历整个数组(一般不会用数组指针干这种事)
注意:我们并不经常这样使用,写在这里只是让你知道确实存在这种用法,后面会介绍常见用法
p指向数组,*p相当于数组名,而数组名又相当于首元素地址,所以*p就是首元素的地址,所以使用*(*p+i)就可以遍历真个数组
我们再来看下面这种用法
这种用法是不是比上面那种简单多了,当然不要因此觉得数组指针很没用了。
当我们将其至少用到二维甚至三维数组上的时候,才能体会到他的强大
数组指针的常见用法(作用于多维数组中)
对于一个二维数组:把每一行当作一个元素,所以二维数组的首元素就是他的第一行,因此它的数组名就是他的第一行。
arr传给函数的就是第一行(作为一个一维数组)的地址,那么函数接收时就需要用一个能指向一维数组的指针p
p指向的就是第一行,p+1就指向的是第二行,以此类推。所以*(p+i)就指的是每个一维数组的数组名(想一想数组指针最简单的用法 int arr[2]={0,1};int (*p)[2]=&arr;之后的*p指的arr这个数组名)
如果你感觉这样写太麻烦,可以用p[i][j]来代替*(*(p + i) + j)两者是等价的
小方法
不管是指针数组还是数组指针,去掉名字,其实就是所代表的类型
如int(*p)[5]:p的类型是int(*)[5]
int *p[5]:p的类型是int *[5]
当然你千万不要用int(*)[5] p;这样的形式来定义p,因为语法不支持。我这么说只是方便你理解