C语言指针与函数
C语言指针函数
C语言指针函数就是函数中用到了指针的函数,主要是有以下两种方式以指针为参数的函数以指针为返回值的函数
指针做函数参数
学习函数的时候,讲了函数的参数都是值拷贝,在函数里面改变形参的值,实参并不会发生改变。如下图:
每个函数都有一个独立的栈区,在函数传参的过程中,是把实参的值拷贝给形参,修改形参的值并不能作用到实参。如果想要通过形参改变实参的值,就需要传入实参的地址,可以通过寻址方式作用到实参上,如下图:
想要修改实参的值,需要传入实参的地址,故想要修改该指针变量的指向需要传入指针变量的地址,也就是二级指针。多级指针中也是依次类推,数据结构中常有二级指针传参。
示例程序| 传参的方式动态申请一维数组
传参的方式修改一级指针的值,需要传入二级指针,通过寻址的方式修改一级指针,如下测试代码:#include #include #include void createArray(int** parray, int arrayNum) { *parray = (int*)calloc(arrayNum,sizeof(int)); assert(parray); } int main() { int* p = NULL; createArray(&p, 3); for (int i = 0; i < 3; i++) { printf("%d ", p[i]); } printf(" "); return 0; }
运行结果如下:
示例程序| 封装函数操作数组
通常在封装函数操作数字类(int ,float,double,…)数组一定要传入数组长度,操作字符串类通常不需要,因为字符串存在字符串结束标记。例如封装遍历数组函数和字符串比较函数,代码如下:#include #include #include #include //等效void printArray(int array[], int arrayNum) void printArray(int* array, int arrayNum) { for (int i = 0; i < arrayNum; i++) { printf("%d ", array[i]); } printf(" "); } int myStrcmp(const char* str1, const char* str2) { int i = 0; int j = 0; //字符串比较从左往右比,找到不同的字符即可得到比较结果 while (str1[i] == str2[j]&&str1[i] != " ") { i++; j++; } return str1[i] - str2[j]; } int main() { int array[5] = { 1,2,3,4,5 }; printArray(array, 5); printf("%d ", myStrcmp("string1", "string")>0); printf("%d ", myStrcmp("string", "string")==0); printf("%d ", myStrcmp("string", "string1")<0); return 0; }
运行结果如下:
当然比较函数你也可以返回0,-1,1,只需要在字符串比较函数中分类讨论下即可。
指针做函数返回值
指针当做函数返回值和普通函数一样,只是返回值类型不同而已,既然返回是一个指针,*指针等效变量,故*函数调用也可以等效变量。把指针当做函数返回值注意项:不要返回临时变量的地址可以返回动态申请的空间的地址可以返回静态变量和全局变量的地址
当函数返回临时变量的地址时,地址中存储的数据随着函数调用完会被回收掉,导致获取垃圾值。如下测试代码:#include int* testFunc() { int number = 1314; return &number; } int main() { int* result=testFunc(); //第一次数据做了保留 printf("%d ", *result); //后续数据被回收了,垃圾值 printf("%d ", *result); printf("%d ", *result); return 0; }
运行结果如下:
在vs开发工具中会友善给予提醒,希望看到这类提醒当做错误处理,及时改善,友善提醒如下:
示例程序| 返回值的方式动态申请一维数组
可以返回动态申请的空间的地址,堆区内存需要调用free函数手动释放,如下测试代码:#include #include int* createArray(int arrayNum) { int* p = (int *)calloc(arrayNum, sizeof(int)); return p; } int main() { int* p = NULL; p = createArray(3); for (int i = 0; i < 3; i++) { printf("%d ", p[i]); } free(p); p = NULL; return 0; }
运行结果如下:
示例程序| 用字符串初始化堆区内存并返回首地址
其实和数字类的操作没什么太大区别,唯一要注意的是字符串申请统计长度用strlen,申请是可见长度加1,拷贝赋值用strcpy完成,如下测试代码:#include #include #include #include char* createArray(const char* str) { //申请长度是可见度长度+1 unsigned int length = strlen(str)+1; char* p = (char *)calloc(length, sizeof(int)); assert(p); //不能直接 p=str,语法没问题但是意义不同 strcpy(p, str); return p; } int main() { char* pstr = NULL; pstr = createArray("coolmoying"); puts(pstr); free(pstr); pstr = NULL; return 0; }
运行结果如下:
C语言函数指针
什么是函数指针
如果在程序中定义了一个函数,那么在运行时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。获取函数地址有以下两种方式:函数名&函数名
既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针。函数指针的唯一作用就是调用函数,函数指针没有++和 –运算
如何创建函数指针
函数返回值类型 (*指针变量名) (函数参数列表);
简单来说一句话,用(*变量名) 替换函数名,剩下的照抄即可,形参名可写可不写就是函数指针变量。如下函数的函数指针创建:
如何通过函数指针调用函数
函数指针可以通过不同的初始化方式,调用除了函数名不同,其他类型相同的所有函数。调用方式有以下两种:直接函数指针名替换函数名去调用函数(*函数指针)替换函数名的方式去调用函数
推荐使用第一种方式,代码看起来比较简单。如下测试代码:#include #include #include #include void test() { printf("Test "); } void test2() { printf("Test2 "); } int Max(int a, int b) { return a > b ? a : b; } void printArray(int(*p)[3], int row, int cols) { for (int i = 0; i < row; i++) { for (int j = 0; j < cols; j++) { printf("%d ", p[i][j]); } printf(" "); } } int main() { //创建函数指针变量 void (*pTest)() = NULL; int(*pMax)(int a, int b) = NULL; //参数名可省略 void (*pprint)(int(*)[3], int, int) = NULL; //函数指针赋值 //两种方式即可 pTest = test; pTest = &test; pMax = Max; pprint = printArray; //函数指针变量调用函数 //两种方式即可 pTest(); (*pTest)(); printf("%d ",pMax(1, 2)); int array[2][3] = { 1,2,3,4,5,6 }; pprint(array, 2, 3); //调用除了函数名不同,其他类型相同的所有函数 pTest = &test2; pTest(); return 0; }
运行结果如下:
回调函数
回调函数就是以函数指针作为某个函数的参数,函数指针比较重要的应用就是回调函数,在Windows SDK,多线程,事件处理中大量用到回调函数。函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。简单讲:回调函数是由别人的函数执行时调用你实现的函数。通俗的讲:你到一个商店买东西,没有货,留给店员电话,有货了,打电话给你,然后你去取货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。如下测试代码:#include #include #include #include void get() { printf("取货成功!!! "); } void wait() { printf("等待售货员电话!... "); } void salesperson(bool flag, void(*Doing)()) { if (flag == true) //有货 { printf("通知取货 "); Doing(); } else //无货 { printf("无货 "); Doing(); } } int main() { //通常回调函数有关联的事件 //这里简单用有无货物来做 salesperson(false, wait); salesperson(true, get); return 0; }
通常salesperson是第三方封装好的,我们只需要实现salesperson函数指针,通过salesperson去调用自己的函数,通常别人设计的回调函数都会绑定事件,目前初步接触了解下。运行结果如下:
C语言万能指针充当函数指针
万能指针充当函数指针使用前必须要强制类型转换,函数指针的类型就是去掉变量名即可 ,如下测试代码:#include #include void test() { printf("调用成功!!! "); } int main() { void* p = test; //正常指针调用:p(); //test类型: void(*)() //强转语法: (类型)(表达式) ((void(*)())p)(); return 0; }
运行结果如下:
复杂函数指针解析
右左法则
首先找到标识符,然后往右看,再往左看,每当遇到圆括号时,就应该调转阅读方向,一旦解析完圆括号里面的所有东西,就跳出圆括号,重复这个过程直到整个声明解析完毕。
示例1| int (*func)(int *p)
首先找到那个标识符,就是func,它的外面有一对圆括号,而且左边是一个*号,这说明func是一个指针,然后跳出这个圆括号,先看右边,也是一个圆括号,这说明(*func)是一个函数,而func是一个指向这类函数的指针,就是一个函数指针,这类函数具有int*类型的形参,返回值类型是 int。
示例2| int (*func)(int *p, int (*f)(int*))
func被一对括号包含,且左边有一个*号,说明func是一个指针,跳出括号,右边也有个括号,那么func是一个指向函数的指针,这类函数具有int *和int (*)(int*)这样的形参,返回值为int类型。再来看一看func的形参int (*f)(int*),类似前面的解释,f也是一个函数指针,指向的函数具有int*类型的形参,返回值为int。
示例3| int (*func[5])(int *p)
func右边是一个[]运算符,说明func是一个具有5个元素的数组,func的左边有一个*,说明func的元素是指针,要注意这里的*不是修饰 func的,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合,因此*修饰的是func[5]。跳出这个括号,看右边,也是一对圆括号,说明func数组的元素是函数类型的指针,它所指向的函数具有int*类型的形参,返回值类型为int。
示例4| int (*(*func)[5])(int *p)
func被一个圆括号包含,左边又有一个*,那么func是一个指针,跳出括号,右边是一个[]运算符号,说明func是一个指向数组的指针,现在往左看,左边有一个*号,说明这个数组的元素是指针,再跳出括号,右边又有一个括号,说明这个数组的元素是指向函数的指针。总结一下就是:func是一个指向数组的指针,这个数组的元素是函数指针,这些指针指向具有int*形参,返回值为int类型的函数。
示例5| int (*(*func)(int *p))[5]
func是一个函数指针,这类函数具有int*类型的形参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素的数组。
示例6| int (*(*(*func)(int *))[5])(int *)
func是一个函数指针,这类函数的返回值是一个指向数组的指针,所指向数组的元素也是函数指针,指向的函数具有int*形参,返回值为int。
实际当中,需要声明一个复杂指针时,如果把整个声明写成上面所示的形式,对程序可读性是一大损害。应该用typedef来对声明逐层分解,增强可读性,如果对typedef不懂的,后续讲解。客观请留步
如果阁下正好在学习C/C++,看文章比较无聊,不妨关注下关注下小编的视频教程,通俗易懂,深入浅出,一个视频只讲一个知识点。视频不深奥,不需要钻研,在公交、在地铁、在厕所都可以观看,随时随地涨姿势。
戴璐自叹不如!姜保红靠陪睡40名官员,一路高升扬州戴璐事件在前段时间闹得沸沸扬扬,戴璐和镖子结婚后,镖子就搬到了女方戴璐家中,这个婚房就空闲了出来,奇怪的是空闲的婚房出现了大量的水电费,让戴璐的丈夫出现了怀疑,找到了物业,调看
值此两会召开,建议国家,对信用卡和网贷该严管了根据央行发布的数据,居民负债率已经超过200万亿元,人均负债高达14。285万。负债占比因1859岁的人大概有8。3亿人左右,则成年人负债率大概为66左右。中国第一张信用卡,发行时
张扬两会访谈冯远征中小学应构建系统的表演教学体系年轻人想做演员?梦想是要有的。但先问问自己的出发点是什么。全国政协委员北京人民艺术剧院院长冯远征接受了新华社记者专访面对如今的艺考热冯远征说应该让现在的孩子们冷静下来冯远征在采访中
70岁普京和卡巴耶娃吵架体操皇后年轻时只用兽皮遮体,美得发光据英国媒体太阳报当地时间3月4日早间的消息称,俄罗斯总统弗拉基米尔普京和他传闻中的长期女友阿丽娜卡巴耶娃(AlinaKabaeva)吵架了,而且吵得还挺凶,按照知情人士的说法从来没
特写从黄金周忙到黄金月中央纪委国家监委网站李灵娜自海南三亚报道2月9日至19日,热线平台共受理涉旅游办件2931件,既有群众咨询,也有投诉举报。我们统计发现,关于景区景点类的反映居多,其次是旅游住宿服务
真正的蜂王浆什么味道?太难吃先别着急扔,正宗的就是这个味儿!说到蜂王浆大家都知道,它是蜂王的专属食物,而也正是得益于这种极为特殊的食物,蜂王的寿命是吃蜂蜜长大的蜜蜂的数十倍甚至上百倍,极强的抗氧化抗衰老功效,让很多顶尖的养生产品在蜂王浆面前
这个税务局干部职工热议2022年度湖南最美税务人红网时刻新闻3月3日讯(通讯员缪宇豪)2月28日,湖南省委宣传部国家税务总局湖南省税务局联合主办税月如歌聚美同行湖南最美税务人发布仪式拉开序幕,彭琼刘金生王晨艺万厚林蒋志华刘永建何
雷锋精神铸心间传承奉献伴我行大合唱学习雷锋好榜样为弘扬雷锋精神,充分调动和激发辖区广大党员群众学雷锋,讲奉献良好氛围,3月3日下午,南阳市官庄工区涧河街道黄山社区开展了弘扬雷锋精神,构建和谐社区,共筑锦绣中国
朝鲜最大的妓院,高丽航空的乘务员,40张图看世界如果将摩尔多瓦125和10硬币放在一起,会得到一张牛头的图片,这是一种在摩尔多瓦文化中具有重要意义的动物。波兰首都华沙的水质由蛤蜊监测。如果水的毒性太大,它们就会关闭,触发器会自动
恶心呕吐不要怕,给娃多喝电解质水家长百问百答小姑娘今天精神多了,她开始跟同学们交流经验。孩子们都说恶心呕吐以后不敢喝白开水,胃里更难受。中午她爸爸给她拿回家两瓶电解质水,她爸爸说店里进了这个电解质水,但是价格高,
每天和双胞胎斗智斗勇,我也太难了我是一个52岁的保姆,目前在一个双胞胎家庭做钟点工,前段时间因为双胞胎做手术,外婆从老家来带孩子。现在俩孩子病好了,上学了,外婆回老家,我又开始了和双胞胎斗智斗勇的日子。下午350
戴维斯背靠背轮休!湖人客场不敌火箭,小波特成赢球功臣!回顾近日湖人队对阵火箭队的比赛,虽然相对于火箭队而言,湖人队的账面实力确实占据着明显的优势,但是在比赛开始之前,人们仍然认为火箭队将会取得最终的胜利。因为戴维斯在本场比赛之中将会因
女人不生孩子就是缺德?生育绑架当休矣张婷(西南科技大学)近日,一位自称国学大师的自媒体人康华兰,痛批不想生育女性的讲课视频在网络上广泛流传,其一个女人,你居然不生孩子,那就是缺德,缺老德了!把别人香火断掉了,这样的因
夫妻间见死不救判故意杀人?江苏海安真实案例,细说案发过程这是一起相对来说,比较特殊的案例。很多人可能第一次听说见死不救,还会判为故意杀人的说法,但法院既然这样宣判了,肯定是有法可依,有事实依据的。有一位网友这样评论该事件世上还有这么笨的
女人白头发变多,是老了的征兆吗?知道这几点,让头发保持光泽!随着年龄的增长,女人的头发会逐渐变得稀疏,白发也会逐渐增多,这是因为人体内的黑色素减少,导致头发失去了颜色。但是,如果女性的白头发变多得过快或过多,这可能是身体某些问题的信号。因此
庞俊旭助攻丁俊晖保住巡回锦标赛参赛资格中新社北京3月23日电2023年斯诺克WST精英赛于北京时间23日在英国收官,凭借00后小将庞俊旭的助攻,中国台球名将丁俊晖保住本赛季巡回锦标赛的参赛资格,将在本月27日迎来与北爱
有的降压药物也会引起性功能障碍,可以这样选性功能障碍,别只想到依靠西地那非这类药物,因为性功能障碍有多种不同的诱因,是牵涉到神经系统内分泌系统心血管系统生殖系统等多个方面的问题,今天我们着重探讨的是高血压和高血压药物引起的
Buha除非欧文选择大幅降薪否则湖人会优先考虑留下现有阵容直播吧3月23日讯湖人随队记者JovanBuha今日撰文分析了球队今夏的人员情况,认为签下欧文的概率不大。JovanBuha表示,据消息人士透露,到目前为止,湖人今夏的计划是留下本
女足欧冠十人大巴黎不敌沃尔夫斯堡新华社巴黎3月22日电(记者肖亚卓)在22日进行的一场女足欧冠联赛四分之一决赛首回合较量中,坐镇主场的巴黎圣日耳曼队在王子公园球场以01不敌德国的沃尔夫斯堡队,中国球员李梦雯进入了
海报岳阳文旅,这个春天火出圈这个春天岳阳文旅就这样火出圈了2023湖南岳阳洞庭渔火季启动奏响整年文旅狂欢的春日序曲岳阳处处都是人游花海满城芳的美景更有豁出去的文旅局长们变着花样为地方旅游代言来,让我们一起跟着
平顶山民营经济走在春天里河南日报客户端记者张建新河南日报社全媒体记者王冰珂一把手术刀蕴含多大价值?位于平顶山市郏县的民营企业河南立科达医疗用品科技有限公司,历经数年研制出低温凝切刀,可以做到在低温状态下边
稳开高走热情爆棚!泉城旅游加速走进春天里超然楼打卡趵突泉畔赏花,济南野生动物世界依旧人潮涌动春光明媚,济南各大景区再现人山人海的热闹场景。在景区门票减免政策的影响下,济南旅游市场强势复苏,并爆出多款网红。在即将到来的五一