二级指针、一维数组与指针,二维数组与指针
共 1237字,需浏览 3分钟
·
2022-05-19 07:55
二级指针
指针可以指向一个普通类型的数据,例如 int、double、char 等,也可以指向一个指针类型的数据,例如 int *、double *、char * 等。
如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针。
假设有一个 int 类型的变量 age,page是指向 age 的指针变量,ppage 又是指向 page 的指针变量,它们的关系如下图所示:
将这种关系转换为C语言代码:
int age =28;
int *page = &age;
int **ppage = &page;
指针变量也是一种变量,也会占用存储空间,也可以使用&
获取它的地址。C语言不限制指针的级数,每增加一级指针,在定义指针变量时就得增加一个星号*
。page 是一级指针,指向普通类型的数据,定义时有一个*
;ppage 是二级指针,指向一级指针 page,定义时有两个*
。
如果我们希望再定义一个三级指针 p3age,让它指向 ppage,那么可以这样写:
int ***p3age = &ppage;
四级指针也是类似的道理:
int ****p4age = &p3age;
实际开发中会经常使用一级指针和二级指针,几乎用不到高级指针。
想要获取指针指向的数据时,一级指针加一个*
,二级指针加两个*
,三级指针加三个*
,以此类推,请看代码:
#include
int main()
{
int a =100;
int *p1 = &a;
int **p2 = &p1;
int ***p3 = &p2;
printf("%d, %d, %d, %d\n", a, *p1, **p2, ***p3);
printf("&p2 = %#X, p3 = %#X\n", &p2, p3);
printf("&p1 = %#X, p2 = %#X, *p3 = %#X\n", &p1, p2, *p3);
printf(" &a = %#X, p1 = %#X, *p2 = %#X, **p3 = %#X\n", &a, p1, *p2, **p3);
return 0;
}
一维数组指针
数组(Array)是一系列具有相同类型的数据的集合,每一份数据叫做一个数组元素(Element)。数组中的所有元素在内存中是连续排列的,整个数组占用的是一块内存。以int arr[] = { 99, 15, 100, 888, 252 };
为例,该数组在内存中的分布如下图所示:
定义数组时,要给出数组名和数组长度,数组名可以认为是一个指针
,它指向数组的第 0 个元素。在C语言中,我们将第 0 个元素的地址称为数组的首地址。以上面的数组为例,下图是 arr 的指向:
数组下标为啥从0开始?
数组下标实际上是每个元素的地址相对于第一个元素地址的偏移量
访问数组元素除了可以通过下标法之外,还可以通过指针法访问。
下标法
for(int i = 0;i < 6;i++)
{
printf("%d ",arr[i]);
//printf("%d ",i[arr]);
}指针法
for(int i = 0;i < 6;i++)
{
printf("%d ",*(arr + i));
}这就是为什么在某些地方大家会看到 i[arr] 这种访问数组元素的方法的原因,实际上下标法就是通过指针法来实现的,只不过编译器帮助我们做了这个操作,简化了操作难度。
指向数组的指针
我们也可以定义一个指向数组的指针,例如:
int arr[] = { 99, 15, 100, 888, 252 };
int *p = arr;
arr 本身可以看做是一个指针,可以直接赋值给指针变量 p。arr 是数组第 0 个元素的地址,所以int *p = arr;
也可以写作int *p = &arr[0];
。也就是说,arr、p、&arr[0] 这三种写法都是等价的,它们都指向数组第 0 个元素,或者说指向数组的开头。
如果一个指针指向了数组,我们就称它为数组指针(Array Pointer)。
数组指针指向的是数组中的一个具体元素,而不是整个数组,所以数组指针的类型和数组元素的类型有关,上面的例子中,p 指向的数组元素是 int 类型,所以 p 的类型必须也是int *
。
反过来想,p 并不知道它指向的是一个数组,p 只知道它指向的是一个整数,究竟如何使用 p 取决于程序员的编码。
使用指针访问数组元素和使用函数名没有任何区别,值得注意的是我们不同通过指针获得数组的大小,但是通过数组名却可以。
printf("%d\n",sizeof(arr)); //数组所占字节数 20 Byte
printf("%d\n",sizeof(p)); //指针所占字节数 4 Byte
也就是说,根据数组指针不能逆推出整个数组元素的个数,以及数组从哪里开始、到哪里结束等信息。不像字符串,数组本身也没有特定的结束标志,如果不知道数组的长度,那么就无法遍历整个数组。
关于数组指针的谜题
假设 p 是指向数组 arr 中第 n 个元素的指针,那么 *p++、*++p、(*p)++ 分别是什么意思呢?
*p++ 等价于 *(p++),表示先取得第 n 个元素的值,再将 p 指向下一个元素。
*++p 等价于 *(++p),会先进行 ++p 运算,使得 p 的值增加,指向下一个元素,整体上相当于 *(p+1),所以会获得第 n+1 个数组元素的值。
(*p)++ 就非常简单了,会先取得第 n 个元素的值,再对该元素的值加 1。假设 p 指向第 0 个元素,并且第 0 个元素的值为 99,执行完该语句后,第 0 个元素的值就会变为 100。
数组名和数组指针的区别
虽然说数组名可以当做指针使用,但实际上数组名并不等价于指针。
数组名代表的是整个数组,具有确定数量的元素
指针是一个标量,不能确定指向的是否是一个数组
数组可以在某些情况下会自动转换为指针,当数组名在表达式中使用时,编译器会把数组名转换为一个指针常量,是数组中的第一个元素的地址,类型就是数组元素的地址类型(通过sizeof也可以看出来)
二维数组指针
二维数组可以理解为每一个元素都是一个一维数组的数组,这样就可以很好的理解二维数组与指针了。
下面定义了一个2行3列的二维数组,并画出了对应的内存模型。
我们可以使用arr[0]获得第0个一维数组,然后再加上一个小标就可以获取到对应的元素,如arr[0][0]获取了第0行第0列的元素。