纳尼?函数还有指针?从入门到入土,一文带你上手函数指针!!!

2023-11-20 19:11

本文主要是介绍纳尼?函数还有指针?从入门到入土,一文带你上手函数指针!!!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 前言
  • 一、指针引子
  • 二、使用步骤
    • 1.取函数地址
    • 2.创建函数指针
    • 3.通过函数指针调用函数的两种方法
  • 三、函数指针进阶
  • 总结


在这里插入图片描述

前言

提示:相信大家在C语言的学习中一定会接触指针这样一个东西,而指针也是新手路上一定要消灭的boss,如果以后还要学习Java的同学更是要注重指针的学习。本文着重介绍函数指针,希望本文对屏幕前的你有所帮助!


提示:以下是本篇文章正文内容,下面案例可供参考

一、指针引子

示例:我们常常接触的指针大多有如下几类:
整形指针-存放整形地址,指向整形
字符指针-存放字符地址,指向字符
数组指针-存放数组地址(注意不是数组首元素地址),指向数组

由以上三个例子,我们能总结指针的共同点:存放某个类型变量的地址,指向那个类型的变量,但是在讲函数指针首先有一个问题:函数也有地址吗?我们用一段简单的代码来验证一下即可。

#include<stdio.h>
int Add(int x,int y)
{return x+y;
}
int main()
{printf("%p\n",&Add);return 0;
}

屏幕上打印出地址:
在这里插入图片描述

所以答案是有的,函数也存在地址,那么也就衍生出了今天的知识点-函数指针。

二、使用步骤

1.取函数地址

我们知道&数组名,取出的是数组的地址。单独一个数组名,取出的是数组首元素的地址。但是对于函数来说:函数名==&函数名

我们代码验证一下(示例):

#include<stdio.h>
int Add(int x,int y)
{return x+y;
}
int main()
{printf("%p\n",&Add);printf("%p\n",Add);return 0;
}

在这里插入图片描述
显然,打印出来的地址是一样的,但是这个时候也会有同学跳出来说:“那数组名和&数组名打印出来的地址还一样呢,但意义明显不一样啊”。但是你想想,函数也没有首元素等其他玩意啊,它就是它本身啊,它也不会出现什么函数首元素啊。

所以再次声明:
在函数指针这一块 函数名==&函数名,它的意义和值,都是一样的

2.创建函数指针

我们知道,数组指针用来存放数组地址,整形指针用来存放整形地址。。。函数指针也不例外,它用来存放函数地址,我们现在定义一个p来存放Add地址,那它的类型怎么创建?我们来看一下具体步骤:
1.p是一个指针对吧,给它一个*是不是必须的 p变成了 * p。为了确保 * 和 p结合(如果没有括号,*或者p有可能会与其他的一些符号结合,具体参见符号优先级)那我在 * p外面加一个括号便于观看也没有问题吧,也就是(*p)

2.那函数总得有参数啊,比如这里是Add(int x,int y)。参数x和y的类型是int
你指针指向的函数是不是要找一下它的参数。所以(*p)(int,int)

3.那函数还有一个性质啊,有没有返回值,要是有的话,类型呢? 这里以Add为例,它是返回int型,所以我们指针也返回int 型 即int(*p)(int,int)

到这里Add函数指针的类型就创建完成啦即为*int(p)(int ,int)

需要注意的是:不同函数的参数类型和返回值类型是不一样的,到时候需要根据不同函数对类型进行转换,这里只是以Add函数为例,其他函数以此类推

ps:一个快速判别类型的方法——去掉变量的名字,剩下的就是类型
代码如下(示例):

	int a = 10;//去掉a 类型intint arr[10] = { 0 };//去掉arr 类型int [10]int(*parr)[10] = &arr;//去掉parr 类型int(*)[10],数组指针,指向一个10int型元素的数组int(*pf)(int, int) = &Add;//去掉pf 类型int(*)(int,int)

3.通过函数指针调用函数的两种方法

法一:
我们平时在调用函数的时候,一般就是函数名( ,)然后把参数传进括号即可,那我们现在有函数指针了呀,指针怎么使用?p不是指向了函数Add嘛,我们用*解引用指针,得到的是地址里的东西,也就是说 *p==Add,用 * p(,)来传参也可以实现Add函数的调用。代码如下:

#include<stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{int ret = Add(2, 3);printf("%d\n", ret);//ret=5int(*p)(int, int) = &Add;//p是一个指向函数Add的指针ret = (*p)(3, 3);//ret=6//p指向Add,对p解引用就是Add//简言之:*p=Add//我们并不总是可以拿到变量,有时是拿到变量的地址//对应函数指针同样的道理,有时不直接给你函数,给你函数地址,就这样调用printf("%d\n", ret);
}

法二:
我们在二.1取函数地址那一块介绍了,在函数指针这一块,函数名==&函数名, 也就是说创建函数指针的时候可以这样写:int(*p)(int, int) = Add,Add是赋给了p啊,你也可以认为:p就是Add。你可以这样理解,法一是int(*p)(int, int) = &Add,是把Add的地址给p,所以用p来调用函数要解引用一下,但是法二p就是Add,那不用解引用了,直接调用。代码如下:

#include<stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{//我们由前面的知识知道:函数add取地址时,add=&addint(*p)(int, int) = Add;//把Add赋给p,这里p即可看做Add//与法一不同的是,法一将&Add赋给p,p是Add的地址,所以要解引用,这里p就可以看做是Add本身,可以不解引用int ret = p(3, 6);printf("%d", ret);
}//如果是为了方便理解,一般是用第一种方法,如果是为了操作方便,可以用第二种方法

三、函数指针进阶

大家来看这样一个代码( * (void(*)() ) 0)(),乍一看非常复杂,我们来细化一下
1 . ( * (void( * )() ) 0)() 我们抽出加粗部分
这是我们熟悉的老朋友:void( * )(),这不就是一个函数指针嘛,该函数无参,返回类型void

2 . (void( * )() ) 0是什么?我们联想一下(int)3.14,不就是对3.14强制类型转换嘛,将3.14这个浮点型强制转换成整形。这里同样的道理,是将整形0强制转换成类型为void( * )()的一个函数指针

3 .现在有了(void( * )() ) 0,我们在这个东西前面加一个 *,这个是什么意思,我们知道(void( * )() ) 0已经被转换成一个指针(指针即地址)了,地址前面加一个 *表示解引用,取出地址里的东西,也就是找到了那个函数

4 .(void( * )() ) 0表示那个函数那再在后面加一个()即是对函数的调用,也就是( * (void(*)() ) 0)()


总结

提示:本文介绍了函数指针的原理和多种使用方法,对于函数指针想要进阶提升的小伙伴一定要认真研读本文中的进阶题目,指针是一个大头,但相信坚持不懈的你一定可以战胜它,加油!

在这里插入图片描述

这篇关于纳尼?函数还有指针?从入门到入土,一文带你上手函数指针!!!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/396805

相关文章

Django中的函数视图和类视图以及路由的定义方式

《Django中的函数视图和类视图以及路由的定义方式》Django视图分函数视图和类视图,前者用函数处理请求,后者继承View类定义方法,路由使用path()、re_path()或url(),通过in... 目录函数视图类视图路由总路由函数视图的路由类视图定义路由总结Django允许接收的请求方法http

MySQL常用字符串函数示例和场景介绍

《MySQL常用字符串函数示例和场景介绍》MySQL提供了丰富的字符串函数帮助我们高效地对字符串进行处理、转换和分析,本文我将全面且深入地介绍MySQL常用的字符串函数,并结合具体示例和场景,帮你熟练... 目录一、字符串函数概述1.1 字符串函数的作用1.2 字符串函数分类二、字符串长度与统计函数2.1

Spring WebClient从入门到精通

《SpringWebClient从入门到精通》本文详解SpringWebClient非阻塞响应式特性及优势,涵盖核心API、实战应用与性能优化,对比RestTemplate,为微服务通信提供高效解决... 目录一、WebClient 概述1.1 为什么选择 WebClient?1.2 WebClient 与

python使用try函数详解

《python使用try函数详解》Pythontry语句用于异常处理,支持捕获特定/多种异常、else/final子句确保资源释放,结合with语句自动清理,可自定义异常及嵌套结构,灵活应对错误场景... 目录try 函数的基本语法捕获特定异常捕获多个异常使用 else 子句使用 finally 子句捕获所

Spring Boot 与微服务入门实战详细总结

《SpringBoot与微服务入门实战详细总结》本文讲解SpringBoot框架的核心特性如快速构建、自动配置、零XML与微服务架构的定义、演进及优缺点,涵盖开发环境准备和HelloWorld实战... 目录一、Spring Boot 核心概述二、微服务架构详解1. 微服务的定义与演进2. 微服务的优缺点三

从入门到精通详解LangChain加载HTML内容的全攻略

《从入门到精通详解LangChain加载HTML内容的全攻略》这篇文章主要为大家详细介绍了如何用LangChain优雅地处理HTML内容,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录引言:当大语言模型遇见html一、HTML加载器为什么需要专门的HTML加载器核心加载器对比表二

一文解密Python进行监控进程的黑科技

《一文解密Python进行监控进程的黑科技》在计算机系统管理和应用性能优化中,监控进程的CPU、内存和IO使用率是非常重要的任务,下面我们就来讲讲如何Python写一个简单使用的监控进程的工具吧... 目录准备工作监控CPU使用率监控内存使用率监控IO使用率小工具代码整合在计算机系统管理和应用性能优化中,监

postgresql使用UUID函数的方法

《postgresql使用UUID函数的方法》本文给大家介绍postgresql使用UUID函数的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录PostgreSQL有两种生成uuid的方法。可以先通过sql查看是否已安装扩展函数,和可以安装的扩展函数

MySQL字符串常用函数详解

《MySQL字符串常用函数详解》本文给大家介绍MySQL字符串常用函数,本文结合实例代码给大家介绍的非常详细,对大家学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录mysql字符串常用函数一、获取二、大小写转换三、拼接四、截取五、比较、反转、替换六、去空白、填充MySQL字符串常用函数一、

从入门到进阶讲解Python自动化Playwright实战指南

《从入门到进阶讲解Python自动化Playwright实战指南》Playwright是针对Python语言的纯自动化工具,它可以通过单个API自动执行Chromium,Firefox和WebKit... 目录Playwright 简介核心优势安装步骤观点与案例结合Playwright 核心功能从零开始学习