目录
前言
1. 指针访问数组
1.1 数组名的含义
1.2 使用指针访问数组
2. 一维数组传参的本质
3. 二级指针
4. 指针数组
4.1 指针数组模拟二维数组
结语
在本篇文章中,我们将要一起来探讨指针与数组之间的关系,以及如何理解指针数组及其运用
讲清楚数组名之前,我们先来看一段代码
#include
int main()
{
int arr[10] = { 0 };
int* p = &arr[0];
printf("%p\n", p);
return 0;
}
在代码中,我们创建了一个数组,还创建了一个指针 p 来接收数组第一个元素的地址。实际上,数组名就是数组首元素的地址,我们还可以用数组名来重新写一遍上面的代码
#include
int main()
{
int arr[10] = { 0 };
int* p1 = &arr[0];
int* p2 = arr;
printf("%p\n", p1);
printf("%p\n", p2);
return 0;
}
运行结果如下:
我们发现数组名和首元素地址打印出来的结果一模一样的,那么我们可以认为:
数组名就是数组首元素的地址,也就是说 arr == &arr[0]
但如果这样的话,会有一个“bug”,当我们用 sizeof 来操作数组名时,结果跟我们的预期并不相符
#include
int main()
{
int arr[10] = { 0 };
printf("%zd\n", sizeof(arr));
printf("%zd\n", sizeof(&arr[0]));
return 0;
}
运行结果如下:
(在X84环境下)
不是说好了 arr == &arr[0] 吗,那为什么结果会不一样呢
其实,“数组名就是数组首元素的地址”这句话没错,但是有两个例外:
所以才会出现上面 sizeof(arr)== 40,而 sizeof(&arr[0])== 4 的情况
明白了这个点之后,我们再来看一段代码:
#include
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("&arr = %p\n", &arr);
printf("&arr[0] = %p\n", &arr[0]);
return 0;
}
运行结果如下:
三个结果一模一样的,这是为什么呢?别急,我们这就来揭晓:
//我们把上面的代码抄下来,再各自加上 1
#include
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("arr+1 = %p\n", arr + 1);
printf("&arr = %p\n", &arr);
printf("&arr+1 = %p\n", &arr + 1);
printf("&arr[0] = %p\n", &arr[0]);
printf("&arr[0]+1 = %p\n", &arr[0] + 1);
return 0;
}
运行结果如下:
我们可以发现:
因为 &arr 取出的是数组的地址,当实现 + 1 操作时,是会跳过整个数组的,因此会 + 40 个字节
在理解了数组名的内涵后,我们就可以很方便地使用指针访问数组了
#include
int main()
{
int arr[10] = { 0 };
size_t sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
int* p = arr;
for (i = 0; i < sz; i++)
{
scanf("%d", p + i);
}
for (i = 0; i < sz; i++)
{
printf("%d", *(p + i));
}
return 0;
}
从上面的代码中我们可以看出:数组名 arr 和指针 p 是等价的,我们平时一般使用的是 arr[i] 来访问数组的元素,那 p[i] 是不是也可以访问数组呢?答案是肯定的
#include
int main()
{
int arr[10] = { 0 };
size_t sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
int* p = arr;
for (i = 0; i < sz; i++)
{
scanf("%d", p + i);
}
for (i = 0; i < sz; i++)
{
printf("%d", p[i]); // 改成p[i]
}
return 0;
}
同理可得:arr[i] 也等价于 *(arr + i)。实际上,数组元素的访问的本质就是转换成首元素的地址加上偏移量来求出元素的地址,然后再解引用来访问具体元素
我们在使用函数时,偶尔会有参数是数组的情况,下面我们来看一下具体例子
#include
void test1(int arr[])
{
printf("%d\n", sizeof(arr));
}
void test2(int* arr)
{
printf("%d\n", sizeof(arr));
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
test1(arr);
test2(arr);
return 0;
}
总结:函数的形参可以写成数组的形式,也可以写成指针的形式
指针变量实质上也是一种变量,是变量就有地址。那么二级指针就是指针变量的指针(指针套娃)
对于二级指针:
指针数组,顾名思义就是存放指针的数组
既然指针数组的每个元素是地址,那么又可以指向其他变量的地址
例子:按照二维数组的形式打印
#include
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* parr[] = { arr1,arr2,arr3 };
int i, j;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
printf("%d ", parr[i][j]); // *(*(parr + i) + j)
}
printf("\n");
}
}
运行结果如下:
原理:
parr[i] 是访问 parr 数组的元素,这时候找到的是一维数组,parr[i][j] 访问的就是一维数组中的元素
该代码可以模拟出二维数组的效果,但和真正的二维数组完全不是一回事。因为它的每一个元素的空间并不是连续的,对于真正的二维数组中元素的空间是连续的。
今天我们一起学习了指针与数组之间的关系,包括二级指针等;如有总结不到位的地方还请多多谅解,若有出现纰漏,希望大佬们看到错误之后能够在私信或评论区指正,博主会及时改正,共同进步!
欢迎各位在评论区友好讨论。如果觉得不错的话,麻烦您点个赞吧,十分感谢!