【进阶】C语言第三课:升级你的指针(2)
创始人
2024-04-22 20:09:01
0

目录

🍇前言🍇:

一、数组参数🤠:

        1.一维数组传参🍈:

        2.二维数组传参🍉:

二、指针参数🤩:

        1.一级指针传参🍊:

         2.二级指针传参🍒:

三、函数指针🤯:

        1.函数指针🥝:

        2.函数指针数组🍍:

四、总结🥳:


🛰️博客主页:✈️努力学习的銮同学

🛰️欢迎关注:👍点赞🙌收藏✍️留言

🛰️系列专栏:💐【进阶】C语言学习

        家人们更新不易,你们的👍点赞👍和👉关注👈真的对我真重要,各位路过的友友麻烦多多点赞关注,欢迎你们的私信提问,感谢你们的转发!

        关注我,关注我,关注我,你们将会看到更多的优质内容!!!


🏡🏡本文重点 🏡🏡

🚅  数组参数  🚃  指针参数  🚃  函数指针🚏🚏

🍇前言🍇:

        有了前面的基础,在这节课中我们继续学习指针的进阶部分知识,继续向更高阶升级我们的指针,希望能对大家的学习有所帮助!

一、数组参数🤠:

        之前我们在编写【井字棋】与【扫雷游戏】时,我们发现常常会需要将【数组】或【指针】作为参数传递给函数,于是我们在复杂情况下还需要考虑函数参数的设计

        1.一维数组传参🍈:

        我们都知道,在对数组进行传参时并不会真实的在内存中创建临时数组,数组名的意义是作为数组内首元素的地址,因此当函数参数为数组名时,实际上传递的是数组中首元素的地址,于是我们可以发现下面三种传参方式都是可行的:

//方式1:标准传参方式
//完整的传递数组内容
void test1(int arr[3])
{int i = 0;for (i = 0; i < 3; i++){printf("%d ", arr[i]);}printf("\n");
}//方式2:省略数组大小
//形参部分的数组大小可以省略
void test2(int arr[])
{int i = 0;for (i = 0; i < 3; i++){printf("%d ", arr[i]);}printf("\n");
}//方式3:扩大形参数组大小
//实际仍为传递首元素地址,故可行,但不建议
void test3(int arr[100])
{int i = 0;for (i = 0; i < 3; i++){printf("%d ", arr[i]);}printf("\n");
}int main()
{int arr[3] = { 1,2,3 };//调用函数:test1(arr);test2(arr);test3(arr);return 0;
}

        这三种将形参写作数组形式的方式都是可行的,但是为了避免出现错误,推荐大家尽可能的使用第一种方式,其次是第二种方式。第三种方式虽然也可以运行,但有可能会出现难以预料的错误,不建议使用

        同时,以上三种将形式参数写成数组形式的写法,也可以改写为使用指针做形式参数的形式

//使用指针作为形式参数:
//一级指针:
void test1(int* p)
{int i;for (i = 0; i < 3; i++){printf("%d ", *(p + i));}printf("\n");
}//二级指针_形式1:
void test2(int** p)
{int i;for (i = 0; i < 3; i++){printf("%d ", *(p + i));}printf("\n");
}//二级指针_形式2:
void test3(int** p)
{int i = 0;for (i = 0; i < 3; i++){printf("%d ", **(p + i));}
}int main()
{int arr[3] = { 1,2,3 };int* parr = &arr;int arrA[3] = { 1,2,3 };int arrB[3] = { 4,5,6 };int arrC[3] = { 7,8,9 };int* arrD[3] = { &arrA,&arrA,&arrC };//调用函数:test1(arr);test2(parr);test3(arrD);return 0;
}

        并且同理,使用指针作为形式参数的另外两种形式也是可以(但最后一种方式同样不建议)的:

void test(int* p[3])
{...
}void test(int* p[100])
//可以但不建议使用
{...
}

        2.二维数组传参🍉:

        我们在前面初阶的部分学习二维数组传参时就知道了,二维数组在进行传参时可以不知道有多好行,但必须知道有多少列,这样计算机才知道应该在何时进行换行。如此只要知道了什么时候进行换行,对于行数就不需要再进行强制要求了,所以在数组进行传参时,允许写成以下三种形式:

//二维数组传参:
//方式1:标准传参
void test1(int arr[3][3])
{int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 3; j++){printf("arr[%d][%d] = %d ", i, j, arr[i][j]);}printf("\n");}
}//方式2:行数可以省略
void test2(int arr[][3])
{int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 3; j++){printf("arr[%d][%d] = %d ", i, j, arr[i][j]);}printf("\n");}
}//方式3:行数可以超出原数组上限
void test3(int arr[100][3])
{int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 3; j++){printf("arr[%d][%d] = %d ", i, j, arr[i][j]);}printf("\n");}
}int main()
{int arr[3][3] = { {1,2,3},{4,5,6},{7,8,9} };//函数调用:test1(arr);test2(arr);test3(arr);return 0;
}

        同样的,二维数组除了可以使用数组作为函数的参数以外,也可以使用指针作为函数的参数进行传参,区别于一维数组,二维数组在传参时传递的不是首元素的地址而是首行元素的地址

void test(int(*p)[3])
//此处的数组大小3为列数
{int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 3; j++){printf("%d ", (*p + i * 3)[j]);//这里注意p+(i*3)是因为i为行号,跳过行时需要跳过i*3个数据元素}printf("\n");}
}int main()
{int arr[3][3] = { {1,2,3},{4,5,6},{7,8,9} };test(arr);return 0;
}

        但是要格外注意,二维数组与一维数组不同,不可以使用二级指针进行传参。其原理是,二级指针的作用是用于存储一级指针的的地址,而传递过来的参数是二维数组第一行(这里可以简单理解为一个一维数组,但本质上不是)的地址,无法使用二级指针进行存储。

二、指针参数🤩:

        1.一级指针传参🍊:

        当我们在函数调用,并使用一级指针作为参数时,很容易理解:一级指针 p 中存放的是数组 arr 中首元素的地址,即传址做参,于是我们就可以在函数参数设计时,使用一级指针进行接收,就可以达到我们的目的。

void test(int* p)
//传递的是一级指针,存储的是arr首元素的地址,使用一级指针进行接收
{int i = 0;for (i = 0; i < 5; i++){printf("%d ", *p + i);}
}int main()
{int arr[5] = { 1,2,3,4,5 };int* p = arr;//数组名为首元素地址test(p);//等价于:test(arr);return 0;
}

         2.二级指针传参🍒:

        首先最基础的用法很好理解,无非是传递二级指针,就使用二级指针进行接收,无需过多阐述。我们直接上示例即可:

void test(int** p)
//传递二级指针,使用二级指针进行接收
{printf("%c", **p);
}int main()
{char a = 'w';char* pa = &a;char** ppa = &pa;test(ppa);return 0;
}

        作为进阶部分的知识,我们就要去研究更深层的东西。我们想一想,当函数参数为二级指针时,都可以接收什么参数?

        我们都知道,二级指针是用来存储一级指针的地址的,那么除了定义二级指针存储一级指针地址,我们在前面还学过一个知识点也是用来存储地址的——指针数组。那么指针数组可以做为函数参数进行传递吗?答案是,可以:

//指针数组做函数参数:
void test(int** p)
{int i = 0;for (i = 0; i < 3; i++){printf("%c ", **p + i);}
}int main()
{char a = 'a';char b = 'b';char c = 'c';char* arr[3] = { &a,&b,&c };test(arr);return 0;
}

三、函数指针🤯:

        1.函数指针🥝:

        我们都知道,在我们的程序中,各种值和组成成分都有自己的一片空间,我们的自定函数也不例外:

void test()
{printf("hehe\n");
}int main()
{printf("%p\n", test);printf("%p\n", &test);return 0;
}

        我们看到哪怕是我们的自定函数,也有着自己的储存空间

        那么当我们想要将函数的地址储存起来时,又该如何进行处理呢?函数指针给出了答案。其定义格式为:

🦑🦑函数返回类型( * + 函数指针名 )(函数参数类型)= 函数名;🦑🦑

        例如:

int Add(int x, int y)
{int z = x + y;return z;
}int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);int ret = Add(a, b);printf("a + b = %d\n", ret);printf("\n");int(*p)(int, int) = Add;//Add函数的返回类型为int类型,函数指针名为p,两个参数类型分别为int类型、int类型printf("%p\n", p);return 0;
}

        我们很清楚的看到,通过使用函数指针就可以将函数的地址存储起来

        并且我们可以通过使用函数指针优化我们的代码,大幅度提升我们代码的可读性

🍱我们先来看一段代码:

void(*signal(int,void(*)(int)(int);

🍝很明显这段代码的可读性非常差,理解起来非常麻烦,于是我们可以通过使用函数指针来提升我们代码的可读性:

typedef void(*pf_t)(int);
pf_t signal(int,pf_t);

        2.函数指针数组🍍:

        对于函数指针,同样可以使用函数指针数组存储多个函数指针

int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;;
}int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);int ret1 = Add(a, b);int ret2 = Sub(a, b);int ret3 = Mul(a, b);printf("ADD = %d SUB = %d Mui = %d\n", ret1, ret2, ret3);//使用函数指针从存放函数地址:int(*padd)(int, int) = Add;int(*psub)(int, int) = Sub;int(*pmul)(int, int) = Mul;//函数指针数组:int(*p[3])(int, int) = { padd,psub,pmul };//通过函数指针数组打印函数地址:int i = 0;for (i = 0; i < 3; i++){printf("指针p[%d]中存放的地址为:%p\n", i, p[i]);}//通过函数指针数组调用函数:for (i = 0; i < 3; i++){int RET = p[i](a, b);printf("%d ", RET);}printf("\n");return 0;
}

四、总结🥳:

        今天我们学习了数组参数、指针参数以及函数指针的相关知识⛵⛵,希望我的文章和讲解能对大家的学习提供一些帮助🌞🌞。各位小伙伴们下去以后可不要疏于练习喔!!!

        🔥🔥通向梦想的路上的确有一道高墙,但它只阻挡不够热爱的人!!!🔥🔥

        辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~  最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

相关内容

热门资讯

电视安卓系统哪个品牌好,哪家品... 你有没有想过,家里的电视是不是该升级换代了呢?现在市面上电视品牌琳琅满目,各种操作系统也是让人眼花缭...
安卓会员管理系统怎么用,提升服... 你有没有想过,手机里那些你爱不释手的APP,背后其实有个强大的会员管理系统在默默支持呢?没错,就是那...
安卓系统软件使用技巧,解锁软件... 你有没有发现,用安卓手机的时候,总有一些小技巧能让你玩得更溜?别小看了这些小细节,它们可是能让你的手...
安卓系统提示音替换 你知道吗?手机里那个时不时响起的提示音,有时候真的能让人心情大好,有时候又让人抓狂不已。今天,就让我...
安卓开机不了系统更新 手机突然开不了机,系统更新还卡在那里,这可真是让人头疼的问题啊!你是不是也遇到了这种情况?别急,今天...
安卓系统中微信视频,安卓系统下... 你有没有发现,现在用手机聊天,视频通话简直成了标配!尤其是咱们安卓系统的小伙伴们,微信视频功能更是用...
安卓系统是服务器,服务器端的智... 你知道吗?在科技的世界里,安卓系统可是个超级明星呢!它不仅仅是个手机操作系统,竟然还能成为服务器的得...
pc电脑安卓系统下载软件,轻松... 你有没有想过,你的PC电脑上安装了安卓系统,是不是瞬间觉得世界都大不一样了呢?没错,就是那种“一机在...
电影院购票系统安卓,便捷观影新... 你有没有想过,在繁忙的生活中,一部好电影就像是一剂强心针,能瞬间让你放松心情?而我今天要和你分享的,...
安卓系统可以写程序? 你有没有想过,安卓系统竟然也能写程序呢?没错,你没听错!这个我们日常使用的智能手机操作系统,竟然有着...
安卓系统架构书籍推荐,权威书籍... 你有没有想过,想要深入了解安卓系统架构,却不知道从何下手?别急,今天我就要给你推荐几本超级实用的书籍...
安卓系统看到的炸弹,技术解析与... 安卓系统看到的炸弹——揭秘手机中的隐形威胁在数字化时代,智能手机已经成为我们生活中不可或缺的一部分。...
鸿蒙系统有安卓文件,畅享多平台... 你知道吗?最近在科技圈里,有个大新闻可是闹得沸沸扬扬的,那就是鸿蒙系统竟然有了安卓文件!是不是觉得有...
宝马安卓车机系统切换,驾驭未来... 你有没有发现,现在的汽车越来越智能了?尤其是那些豪华品牌,比如宝马,它们的内饰里那个大屏幕,简直就像...
p30退回安卓系统 你有没有听说最近P30的用户们都在忙活一件大事?没错,就是他们的手机要退回安卓系统啦!这可不是一个简...
oppoa57安卓原生系统,原... 你有没有发现,最近OPPO A57这款手机在安卓原生系统上的表现真是让人眼前一亮呢?今天,就让我带你...
安卓系统输入法联想,安卓系统输... 你有没有发现,手机上的输入法真的是个神奇的小助手呢?尤其是安卓系统的输入法,简直就是智能生活的点睛之...
怎么进入安卓刷机系统,安卓刷机... 亲爱的手机控们,你是否曾对安卓手机的刷机系统充满好奇?想要解锁手机潜能,体验全新的系统魅力?别急,今...
安卓系统程序有病毒 你知道吗?在这个数字化时代,手机已经成了我们生活中不可或缺的好伙伴。但是,你知道吗?即使是安卓系统,...
奥迪中控安卓系统下载,畅享智能... 你有没有发现,现在汽车的中控系统越来越智能了?尤其是奥迪这种豪华品牌,他们的中控系统简直就是科技与艺...