二级指针、一维数组与指针,二维数组与指针

ACM比赛整理

共 1330字,需浏览 3分钟

 · 2022-02-25

二级指针

指针可以指向一个普通类型的数据,例如 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列的元素。

浏览 25
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报