【C语言】指针3——数组与指针(超级详解!包懂)

指针3——数组与指针
Ciallo~ (∠・ω< )⌒★
在这里插入图片描述

1. 一维数组

1.1 数组名的理解(辨析)

以下面这段代码为例

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int* p = &arr[0];

我们通过取地址操作符 &,拿到了 arr 数组首元素的地址。其实,数组名就是数组首元素的地址

所以若我们想要拿到 arr 首元素的地址,可以这样写:

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int* p = arr;

但是有两个例外,数组名表示整个数组:
1. sizeof(数组名)
2. &数组名

解释:

  1. sizeof是用来计算大小的,那么仍然以上面的代码为例 sizeof(arr) 计算得到的是整个数组的大小,所以 sizeof(arr) = 40
  2. &数组名,这样取出来的是整个数组的地址。我们可以这样打印观察:
#include <stdio.h>
int main()
{
    int arr[5] = {1,2,3,4,5};
    printf("%p\n", &arr);
    printf("%p\n", &arr + 1);
    return 0;
}

运行结果是:

在这里插入图片描述

可以算出两个地址相差 20 个字节,这正是一个 arr 数组的大小,由此可证明,&arr 取出的是整个数组的地址。本质上,数组的地址就是一个数组指针。
除此之外,任何地方使用数组名,数组名都表示首元素的地址。

1.2 使用指针访问一维数组

因为数组在内存中是连续存放的,所以可以通过指针加减整数的操作,得到每个元素的地址,进而能解引用访问数组元素。

#include <stdio.h>
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p = arr;
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", *(p + i));
    }
    return 0;
}

运行结果:

在这里插入图片描述

若这样写,也是可以正常打印的

#include <stdio.h>
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p = arr;
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", p[i]);
    }
    return 0;
}

本质上 p[i] 是等价于 * (p+i)的。
同理 arr[i] 应该等价于 * (arr+i),数组元素的访问在编译器处理的时候,也是转换成首元素的地址 + 偏移量求出元素的地址,然后解引用来访问的。

1.3 一维数组的传参

对于一维数组的传参,形参部分我们之前是这样写的:

void test(int arr[]) //参数写成数组形式,本质上还是指针 
{
 	printf("%d\n", sizeof(arr));
}

本质上数组传参传递的是数组首元素的地址
所以可以改写成这样:

void test(int* arr)//参数写成指针形式 
{
 	printf("%d\n", sizeof(arr));//计算⼀个指针变量的⼤⼩ 
}

一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。

2. 数组指针与指针数组(辨析,要分清!)

2.1 数组指针

数组指针就是用来存放数组的地址的指针变量,定义方法如下:

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

&arr 取出了整个数组的地址,放到一个数组指针里去。* 与 p 结合,表示 p 是一个指针,指向了有5个元素,每个元素为 int 类型的数组。
需要加()是因为 [ ] 的结合性比 * 高

2.2 指针数组

指针数组是用来存放指针的数组,语法如下:

int* arr[5];

int* 指明了类型,这是一个有着5个元素,每个元素是整型指针的数组。arr 是首元素地址,所以是指针变量的地址,要用二级指针来接收。

3. 二维数组

3.1 二维数组的本质(核心)

书上要求我们在定义一个二维数组时,可以不指定行数,但必须要指定列数。这是为什么呢?其实是因为二维数组的每个元素都是一个一维数组,这个一维数组的元素个数必须被告知。

int arr[3][5] = { {1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15} };

以此为例,arr 的首元素是 {1,2,3,4,5} 这个一维数组,而不是 1。数组的地址可以用数组指针来表示
先来一组辨析请看官享用:

  1. arr:表示首元素地址,即 {1,2,3,4,5} 这个数组的地址
  2. arr[0]: 拿出了这个数组的首元素,是 {1,2,3,4,5} 这个数组的数组名
  3. printf(“%d”, *arr[0]): 第一行的数组名即没有单独放在 sizeof 里,也没有前面跟 &,因此表示数组首元素的地址,这里会打印 1
  4. sizeof(arr[1]):arr[1]是第二行的数组名,单独放在了 sizeof 里,表示第二行整个数组,大小计算为 4 * 5 = 20

3.2 使用指针访问二维数组(重理解)

已知 arr 的每个元素是一个一维数组,我们自然地猜想到要解引用两次。
由前面的内容可知,arr[i] 可改写为 * (arr + i),我们刚刚分析了,arr 在这种情况下表示首元素地址,则 arr + i 表示的就是第 i 行的地址,每一行都是一个一维数组,因此arr + i,表示的是某个一维数组的地址,对它解引用,即 * (arr + i),就得到了这个一维数组。
那么我们再解引用一次,(* (arr + i) + j),就得到了第 i 行数组的第 j 个元素。

这里确实有些抽象,不过看官放心,详解包懂不只是吸睛的标签,还是实在的内核。
君可以无条件信任火焰柚子(bushi
可以这样理解,看官请看;

就以 arr 为例
arr表示首元素地址,是第一行数组的地址,则等价于 &arr
*arr 就把 & 拿掉,得到了 arr,它表示第一行数组首元素的地址,类型是 int*
再解引用一次,*(*arr + j),就等价于 *(arr + j),它表示第一行第j列的元素

我们一定要牢记这个东西:本质上 p[i] 是等价于 * (p+i) 的。

arr[i][j] 就可以改写成 *(*(arr + i) + j)
实际上二维数组也确实是这么进行访问和调用的

3.3 二维数组的传参

对于二维数组的传参,之前我们是这样写的:

void test(int arr[][5]) //参数写成数组形式,本质上还是指针 
{
 	printf("%d\n", sizeof(arr));
}

现在我们了解原理之后,就可以这样写:

void test(int (*p)[5]) //参数写成指针 
{
 	printf("%d\n", sizeof(arr));
}

因为数组名即表示首元素的地址,第一行数组的地址即是一个数组指针,那么形参部分,我们就可以写成数组指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值