前言:
昨天把指针最为基础的内容讲完了,并且详细说明了传值调用和传址调用的区别(这次我也是做到了每日一更,感觉有好多想写的但是没有写完),下面不多废话,下面进入本文想要说的内容
目录:
1.数组名的理解
1.1数组名代表着什么
1.2.数组名的几种特殊情况
2.使用指针访问数组
2.1指针如何访问数组
3.一维数组传参的本质
4.冒泡排序算法的实现(先预热一下,这个太重要我写在下一篇了)
5.二级指针相关知识点
5.1二级指针是什么
5.2二级指针中的解引用操作
6.指针数组相关知识点
6.1指针数组
6.2利用指针数组模拟二维数组
正文:
1.数组名的理解
1,1数组名是什么
其实数组名我在之前就解释过了,现在我们正式的来介绍它,数组名是数组首元素的地址,通过代码的撰写就可以看出来二者是一样的,代码以及展示图如下:
#include<stdio.h>
int main()
{
int arr[10] = {0};
int *p = &arr[0];
printf("%p",arr);
printf("%p",p);
return 0;
}
在说上面之前,我想起来我忘记说%p这个占位符的作用了,这个占位符是代表着地址的占位符,想要打印地址就需要它,不难看出,上面两个地址打印出来的结果是一样的,所以可以更好的理解数组名就是数组首元素的地址,不过,这也不是绝对的,下面一种特殊情况数组名就不是数组第一个元素的地址。
1.2数组名的几种特殊情况
1.2.1sizeof(arr)
我们知道,sizeof运算符是用来计算长度的,上面讲了,arr指的是数组第一个元素的地址,按道理来讲,我们计算arr的长度应该就是第一个元素的长度即四个字节,但是看见下面的代码和打印结果:
#include<stdio.h>
int main()
{
int arr[10] = {0};
printf("%zd",sizeof(arr));
return 0;
}
我们不难看出,这里打印的结果是40个字节,为什么会打印出40呢?因为这是arr整体的元素个数,所以此时sizeof(arr)并不是第一个元素的长度,而是整个元素的长度,这个一定要记住,在某些企业的笔试题中或者正常的考试题中可能会出一个类似这样的题来迷惑你,所以我们要记住这个特殊情况! 不要出错,不要出错,不要出错!
1.2.2&arr
上面小编说过,arr是数组名,代表着数组第一个元素的地址,那么我们如果直接对arr去地址呢?这种情况我们先通过代码打印看一下:
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);
printf("%p\n", &arr[0]);
printf("%p\n", &arr);
return 0;
}
看到这里可能有些读者朋友会想,这三个的地址不都是相同的吗?是不是&arr就是数组第一个元素的地址呢?当然,如果真一样的话,我写这个小标题就一点意义都没有了,先不要着急,我们再看一下下面这个代码就可以知道&arr到底指的是什么了:
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr + 1);
printf("%p\n", &arr[1]);
printf("%p\n", &arr + 1);
return 0;
}
这个时候就可以很清楚的发现三者的不同了,前两个肯定是一样的(前面的知识点的运用),而第三个,经过我们的计算,发现比前两个多了36个字节的内存空间 ,这是为什么呢?我也不卖关子了,其实取地址arr取到的是整个数组的地址,所以我们在进行加1操作的时候,其余都是加了一个字节,而&arr是跳跃了一个完全的数组,从而我们在想要取到地址的时候,一定要知晓取到的应该是一个完整的数组地址,不过刚开始指向的是数组第一个元素的地址罢了,在进行加减的时候一定要小心。
小结:
我们在使用数组名的时候一定要注意这两个特殊情况,除了这两个特殊情况意外,其余的数组名都是代表首元素的地址
2.使用指针去访问数组
2.1指针如何访问数组
其实用指针数组我在上一节课的代码中就已经实现了,以后我还是得改掉超前讲的坏毛病,不多废话,我们知道数组名就是数组第一个元素的地址,所以我们在使用输入函数的时候便可以用数组名来替代&arr[i]类似这样的写法,这样可以减少我们在使用scanf函数的时候忘记运用取地址符的尴尬情况(小编就时常忘记写这个,我还抱怨是编译器的问题,😅),在打印的时候我们就可以利用解引用操作符来实现操作,光说不练等于白说,下面来进行一个久违的环节,小小的例题
例1.利用指针访问数组随机打印五个数
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int i = 0;
for (i = 0; i < 10; i++)
{
scanf("%d", arr + i);
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(arr + i)); //在这里arr[i] == *(arr + i),甚至还可以写成[i]arr,我们在这里可以看出[]仅仅就是一个运算符罢了,不过为了视觉效果所以才这么用的
}
return 0;
}
可以看出这个功效和我们正常使用一位数组是一样的,不过正如我前面所说的,这样写可以避免一些错误的出现,这也让我们之后写数组的方式变得更多了 ,一定要合理运用(scanf函数会时时常忘记写的读者朋友们可以这么写呦~)。
3.一维数组传参的本质
数组我们是知道的,函数我们也是知道的,但是我们之前在利用函数传数组的参数的时候,我们也是传参的数组名,当时我们并没有考虑到数组名到底是干什么的,也不知道数组名就是数组第一个元素的地址,现在根据我们前面讲过的知识,下面小编出一个小小的题目,我们可以不可以通过函数来计算数组元素的个数呢?读者朋友们可以先思考一下,下面是相关代码:
#include<stdio.h>
void ceshi(int arr[10])
{
printf("%zd", sizeof(arr));
}
int main()
{
int arr[10] = { 0 };
ceshi(arr);
return 0;
}
可以看出打印出来的结果是8(我这是在X64环境下写的),不难看出,此时打印的数组元素的地址的大小,并没有打印出整个元素的字节长度,原因很简单,我们知道arr是数组名,代表的是第一个元素的地址,所以我们传过去的是地址,计算长度的时候自然就是计算地址的长度了!所以我们每次想要传数组的时候,需要把数组的个数也传递过去。对于数组传参,其实是有两种形式的,下面来展示一下:
#include<stdio.h>
void ceshi1(int arr[10])
{
//我就不写内容了,看外观别看内容
}
void ceshi2(int* arr)
{
//这两个是一样的,小编比较喜欢写第一个的形式(说实在的这个形式在我复习的时候我才记着有)
}
int main()
{
int arr[10] = { 0 };
ceshi1(arr);
ceshi2(arr);
return 0;
}
正如我在代码页所写的,两种形式均可,可以按着自己习惯来进行书写,第二个可以更好的展示为什么字节数是4 / 8个,因为这本来就是计算地址的长度!
小结:
对于这部分的知识点,大家一定要好好掌握(客套话),下面进入二级指针(冒泡排序我觉着单独出一篇来写去,感觉写在中间有点显的它不重要) 。
4.冒牌排序算法的实现(下一篇见喽)
5.二级指针相关知识点
5.1.二级指针是什么
我们知道,指针变量也是变量,同样也是有地址的,对于普通变量地址的存放是在指针里面,那么指针变量的地址应该存放到哪里呢,显然易见,它会放到二级指针里面,所以用大白话来讲,二级指针就是指针的指针,对于它如何进行初始化,下面献上代码:
#include<stdio.h>
int main()
{
int a = 20;
int* p = &a;
int** m = &p; //可以这么记,有多少个*就代表着几级指针在初始话的时候
return 0;
}
正如我上问所说,经过我的观察,我发现*数决定着几级指针,其实二级指针目前我也没有设计很多,大多数题目设计到的都是一级指针,二级指针先知道大概就行,小编觉着这个在后期(数据结构,我还没学不知道大概)使用的应该更广一点。
5.2.二级指针的解引用操作
我们知道,在一级指针的时候进行解引用操作的时候,会直接取到这个数的本身,所以我们类比二级指针,在二级指针进行解引用的时候,会直接对应到一级指针的内容,也就是一级指针对应元素的地址,详情的代码如下:
#include<stdio.h>
int main()
{
int a = 20;
int* p = &a;
int** m = &p;
printf("%p\n", p);
printf("%p", *m);
return 0;
}
从上面可以很显然的看出二者是相同的,这更加的证实了我上面所说,对二级指针进行第一次解引用的时候,得出的是一级指针的详情内容,划重点(第一次),所以有一必有二,二级指针也可以进行二次解引用,试想一下,第一次解引用的时候取到的是一级指针,对二级指针进行解引用的时候就是对一级指针再次进行解引用,得到了数的本身,很有趣吧,知识都是环环相扣的,下面是代码展示:
#include<stdio.h>
int main()
{
int a = 20;
int* p = &a;
int** m = &p;
**m = 12;
printf("%d\n", a);
printf("%d\n", **m);
return 0;
}
上面的代码很好的证实了我刚才说的正确性,所以二级指针是可以进行二次解引用的,当然,除了二级指针,还有三级指针,四级指针等等 ,不过这些都不经常使用,所以,指针记住一级指针=和二级指针就好,我们记住对我们有用的,记太多是容易遗忘的。
小结:
二级指针的知识已经全部都讲完了,希望读者朋友们好好的理解,下面进入最后一小节喽!
6.指针数组相关知识点
6.1指针数组
在讲到这个之前,记住这是指针数组,一定要跟后期的数组指针区分开, 这俩真容易弄混。好了,进入正题,指针数组这个名字,可以类比记忆,类似整型数组,浮点型数组,顾名思义,这是个存放指针的数组,数组中的每一个元素都是指针(地址),下面来介绍一下它是如何进行初始化的:
int * arr[5] = {0};
int代表的是类型,这一个不唯一,我只是当作例子展示一下,也可以是float,double等等,* 代表的是指针类型,arr是指的名字,arr[5]代表着存了五个地址,一定要记住它的写法,后期有很多指针类型等着我们记,我们一定不要搞混,这便是指针数组的初始化,下面可以进入一个小小的案例:用指针数组来模拟实现二维数组(小重点)!
6.2.利用指针数组模拟二维数组
二维数组我们都知道(这部分我没有出博客,因为我有点懒),我们可以通过我们刚刚了解到的指针数组来进行模拟实现二维数组,下面先呈上代码供各位观看(代码下面是讲解
#include<stdio.h>
int main()
{
int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 1,2,3,4,5 };
int arr3[5] = { 1,2,3,4,5 };
int* arr[] = { arr1,arr2,arr3 }; //存放的都是数组第一个元素的地址,存放地址的叫做指针数组
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", *(*(arr + i) + j)); //也可以写成arr[i][j]
//上面这个是挨着访问的,具体理解我写到博客里面,在这里我不好展开
}
printf("\n");
}
return 0;
}
下面我们来对上面的代码进行更详细的解释 ,首先我们先要设置三个数组,里面都放着想对应的元素,是为了后续二维数组更加的完善,我们先把每个数组的首地址放到指针数组里面,为了可以把每个元素都打印出来,我们设置了两层循环,外层循环三次的原因是因为先选好每一个数组是什么,就比如i是0的时候,这时候开始访问第一个数组的地址,我们之后在进行循环就可以打印出第一个数组的内容了,这里的知识点涉及到了上面小编写过的《使用指针访问数组》就写到了如何用指针来写出数组,之后每次i++就会打印每一行数组的内容了,然后我们便模拟出了二维数组,小编画了两张抽象图片供读者理解:
这个图虽然有点抽象,但我也画出了大概的雏形,就比如右图代表着设置了三个数组,左边代表着指针数组里面包含着三个数组首元素的地址。
小结:
数组指针是指针中较为重要的知识点,读者朋友们一定要好好的领悟这个,不然后期真的很容易和数组指针弄混,小编建议大家可以看完文章后敲一下我在这篇文章写过的代码一定会有收获的!
总结:
可算是写完这篇文章了,这篇文章可以看作是指针的进阶篇,指针从这便开始上强度了,大家一定要好好的学会指针,学会指针C语言便会打下半壁江山,加油,以后大厂的程序员们!如果文章出现了差错,欢迎从评论区指出,希望各位可以多点点赞,您的点赞便是我进步的动力,我们下一篇见啦!