hello,各位小伙伴,本篇文章跟大家一起继续深入学习指针,感谢大家对我上一篇的支持,如有什么问题,还请多多指教
如果本篇文章对你有帮助,还请各位点点赞!!!
本篇主要学习数组在指针里的使用 ,话不多说,进入正题
1. 数组名的理解
想必大家对数组并不陌生,但是也有不少萌新并没有熟悉数组名的意思,来看如下代码:
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
printf("%p\n",p);
printf("%p\n",arr);
p已经得到了arr[0]的地址,打印p就是打印arr[0]的地址,那么下一行打印arr是什么东西,是把整个数组的地址,给打印出来吗?并不是,在这里,arr代表数组首元素的地址,所以这两个打印出来的是同一个地址,都为arr[0]的地址。
咱们再举一个例子:
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d\n",sizeof(arr));
这时候有小伙伴说:arr不就是数组首元素地址嘛,所以打印出来的结果就是4或者8嘛
注:编译器在不同环境下,地址大小所占字节不一样
但事实上并不是,打印出来的结果是:40
这里的arr表示的是整个数组,那么整个数组的大小,不就是这个数组所占用的空间吗,所以答案40 = 4(每个元素占4个字节) X 10(数组申请了10个元素的空间) ,就有小伙伴蒙了,这该怎么区分arr什么时候表示首元素地址、什么时候表示整个数组呢?其实并不难区分
其实数组名就是数组⾸元素(第⼀个元素)的地址是对的,但是有两个例外:
• sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的大小, 单位是字节
• &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素 的地址是有区别的)
除此之外,任何地方使用数组名,数组名都表示首元素的地址。
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] = %p\n", &arr[0]);
printf("arr = %p\n", arr);
printf("&arr = %p\n", &arr);
return 0;
}
这三个打印结果都一样,有小伙伴疑惑:那么是不是&arr就完全与arr一样了?
还真不一样,干说难懂,看如下代码 :
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] = %p\n", &arr[0]);
printf("&arr[0]+1 = %p\n", &arr[0]+1);
printf("arr = %p\n", arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr = %p\n", &arr);
printf("&arr+1 = %p\n", &arr+1);
return 0;
}
可以看到,打印出来的结果都为地址,有些地址一样,有些地址不一样
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] = %p\n", &arr[0]);//取出arr[0]的地址
printf("&arr[0]+1 = %p\n", &arr[0]+1);//取出arr[0]的地址,并以arrr[0]为单位对该地址进行+1操作,也就是得到arr[1]的地址
printf("arr = %p\n", arr);//取出arr的地址,就是数组首元素arr[0]的地址
printf("arr+1 = %p\n", arr+1);//取出整个数组的地址,并以一个元素为单位对该地址进行+1操作,即得到arr[1]的地址
printf("&arr = %p\n", &arr);//取出arr的地址,也就是数组首元素的地址————arrr[0]的地址
printf("&arr+1 = %p\n", &arr+1);//取出整个数组的地址,并以整个数组为单位对该地址进行+1操作
return 0;
}
只要数组名前面有&或者sizeof就表示整个数组,除此之外都表示数组首元素
那么&arr+1就是数组的地址以整个数组为单位,进行+1操作,在上述例子中,从arr[0]的地址跳过了40个字节
相应的,&arr[0] +1就表示着:取出arr[0]的地址,并以arrr[0]为单位对该地址进行+1操作,也就是得到arr[1]的地址,跳过了4个字节
arr+1也不例外,不过意思不一样: 取出整个数组的地址,并以一个元素为单位对该地址进行+1操作,即得到arr[1]的地址,跳过了4个字节
2. 使用指针访问数组
根据前面所学的,我们就可以很⽅便的使⽤指针访问数组了,代码说话:
#include <stdio.h>
int main()
{
int arr[10] = {0};
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);//计算一共需要打印多少个元素
int* p = arr;
for(i=0; i<sz; i++)
{
scanf("%d", p+i);//指针+1,即以指针为单位进行+1操作
//scanf("%d", arr+i);//也可以这样写
}
for(i=0; i<sz; i++)
{
printf("%d ", *(p+i));
}
return 0;
}
arr+1 = p+1,思索片刻,那么:arr[0] ?= p[0]
实践出真知
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
//输⼊
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
//输⼊
int* p = arr;
for (i = 0; i < sz; i++)
{
scanf("%d", p + i);
//scanf("%d", arr+i);//也可以这样写
}
//输出
for (i = 0; i < sz; i++)
{
printf("%d ", p[i]);
}
return 0;
}
在第18行的地方,将*(p+i)换成p[i]也是能够正常打印的,所以本质上p[i] 是等价于 *(p+i)。
同理arr[i] 应该等价于 *(arr+i),数组元素的访问在编译器处理的时候,也是转换成⾸元素的地址+偏移 量求出元素的地址,然后解引⽤来访问的。
3. ⼀维数组传参的本质
⼀维数组传参的本质其实就是将数组的地址传过去,让我们来看看下面的代码:
#include <stdio.h>
void test(int arr[])
{
int sz2 = sizeof(arr)/sizeof(arr[0]);
printf("sz2 = %d\n", sz2);
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int sz1 = sizeof(arr)/sizeof(arr[0]);
printf("sz1 = %d\n", sz1);
test(arr);
return 0;
}
sz1和sz2仿佛都是在求数组里元素的个数,但sz2真的能够得到数组里与那稣的个数吗?
注:这是X86环境下得到的结果,X64环境下sz2结果为2
显然并不能,上个小节我们学习了:数组名是数组首元素的地址;那么在数组传参的时候,传递的是数组名,所以:本质上数组传参本质上传递的是数组首元素的地址。
所以函数形参的部分理论上应该使⽤指针变量来接收⾸元素的地址。那么在函数内部我们写 sizeof(arr) 计算的是⼀个地址的大小(单位字节)而不是数组的大小(单位字节)。正是因为函数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。
在传参的时候,我们有两种写法:
void test(int arr[])//参数写成数组形式,本质上还是指针
{
printf("%d\n", sizeof(arr));
}
void test(int* arr)//参数写成指针形式
{
printf("%d\n", sizeof(arr));//计算⼀个指针变量的⼤⼩
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
test(arr);
return 0;
}
总结:⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。
4. 冒泡排序
冒泡排序的核心思想就是:两两相邻的元素进行比较
示例代码:
void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{
int i = 0;
for(i=0; i<sz-1; i++)
{
int j = 0;
for(j=0; j<sz-i-1; j++)
{
if(arr[j] > arr[j+1])
{
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
int main()
{
int arr[] = {3,1,7,5,8,9,0,2,4,6};
int sz = sizeof(arr)/sizeof(arr[0]);
bubble_sort(arr, sz);
for(i=0; i<sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
5. ⼆级指针
二级指针,就是存放指针地址的指针。
我们都说指针变量存放地址,那么指针变量也是变量,它也有地址,我们再创建一个指针来存放指针的地址:
int a = 10; //创建a变量;
int *p1 = &a; //指针p1存放a的地址;
int **p2 = p1; //指针p2存放指针p1的地址;
p2就是二级指针,它的类型为int**,我们可以拆分来看:
int * *p 红色的*是为了告诉编译器,这是一个指针;
int * *p 红色的*是为了告诉编译器,这个指针指向的类型为整型指针
int a = 10;
int *p1 = &a;
int **p2 = p1;
printf("%d\n",*p2);//*p2其实访问的就是p1
printf("%d\n",**p2);//**p2其实就是找到p1,然后再对p1进行解引用,找到a
6.指针数组
首先,我们要搞明白,指针数组到底是个什么东西。举个例子:红色苹果,本质上是红色还是苹果?那肯定是苹果,那么指针数组,肯定是数组嘛
就和整型数组一样,整型数组存放的是整形;那指针数组存放的就是指针啦
结合上述所学,我们可以用指针数组来模拟二维数组:
#include <stdio.h>
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数组中
int* parr[3] = {arr1, arr2, arr3};
int i = 0;
int j = 0;
for(i=0; i<3; i++)
{
for(j=0; j<5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}
二维数组其实就是数组存放数组,那么我用指针数组来存放数组的地址,不久能够模仿出二维数组了吗。解析图:
parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型⼀维数组,parr[i][j]就是整型⼀维数 组中的元素。
上述的代码模拟出⼆维数组的效果,实际上并非完全是⼆维数组,因为每一行并非是连续的
好啦,本篇文章对于指针就讲到这里,如果有什么问题,还请指教指教,希望本篇文章能够对你有所帮助,我们下一篇见!!!
如你喜欢,点点赞就是对我的支持,感谢感谢!!!