函数指针数组定义例子_函数指针数组定义例子有哪些

函数指针数组定义例子_函数指针数组定义例子有哪些c语言函数指针的理解与使用,收藏起来继承!1.函数指针的定义顾名思义,函数指针就是函数的指针。它是一个指针,指向一个函数。看例子:A) char * (*fun1)(char * p1,char * p2);B) char * *fun2(char

c语言函数指针的理解与使用,收藏起来继承!
  1.函数指针的定义

  顾名思义,函数指针就是函数的指针。它是一个指针,指向一个函数。看例子:

  A) char * (*fun1)(char * p1,char * p2);

  B) char * *fun2(char * p1,char * p2);

  C) char * fun3(char * p1,char * p2);

  看看上面三个表达式分别是什么意思?

  C)这很容易,fun3是函数名,p1,p2是参数,其类型为char *型,函数的返回值为char *类型。

  B) 也很简单,与C)表达式相比,唯一不同的就是函数的返回值类型为char**,是个二级指针。

  A) fun1是函数名吗?回忆一下前面讲解数组指针时的情形。我们说数组指针这么定义或许更清晰:

  1int (*)[10] p;

  再看看A)表达式与这里何其相似!明白了吧。这里fun1不是什么函数名,而是一个指针变量,它指向一个函数。这个函数有两个指针类型的参数,函数的返回值也是一个指针。同样,我们把这个表达式改写一下:

  1char * (*)(char * p1,char * p2) fun1;

  这样子是不是好看一些呢?只可惜编译器不这么想。^_^。

  2.函数指针使用的例子

  上面我们定义了一个函数指针,但如何来使用它呢?先看如下例子:

  #include <stdio.h>

  #include <string.h>

  char * fun(char * p1,char * p2)

  {

  int i = 0;

  i = strcmp(p1,p2);

  if (0 == i)

  {

  return p1;

  }

  else

  {

  return p2;

  }

  }

  int main()

  {

  char * (*pf)(char * p1,char * p2);

  pf = &fun;

  (*pf) ("aa","bb");

  return 0;

  }

  我们使用指针的时候,需要通过钥匙(“*”)来取其指向的内存里面的值,函数指针使用也如此。通过用(*pf)取出存在这个地址上的函数,然后调用它。

  这里需要注意到是,在Visual C++6.0里,给函数指针赋值时,可以用&fun或直接用函数名fun。这是因为函数名被编译之后其实就是一个地址,所以这里两种用法没有本质的差别。这个例子很简单,就不再详细讨论了。

  3.*(int*)&p —-这是什么?

  也许上面的例子过于简单,我们看看下面的例子:

  void Function()

  {

  printf("Call Function!\n");

  }<br>

  int main()

  {

  void (*p)();

  *(int*)&p=(int)Function;

  (*p)();

  return 0;

  }

  这是在干什么?*(int*)&p=(int)Function;表示什么意思?

  别急,先看这行代码:

  1void (*p)();

  这行代码定义了一个指针变量p,p指向一个函数,这个函数的参数和返回值都是void。

  &p是求指针变量p本身的地址,这是一个32位的二进制常数(32位系统)。

  (int*)&p表示将地址强制转换成指向int类型数据的指针。

  (int)Function表示将函数的入口地址强制转换成int类型的数据。

  分析到这里,相信你已经明白*(int*)&p=(int)Function;表示将函数的入口地址赋值给指针变量p。

  那么(*p) ();就是表示对函数的调用。

  讲解到这里,相信你已经明白了。其实函数指针与普通指针没什么差别,只是指向的内容不同而已。

  使用函数指针的好处在于,可以将实现同一功能的多个模块统一起来标识,这样一来更容易后期的维护,系统结构更加清晰。或者归纳为:便于分层设计、利于系统抽象、降低耦合度以及使接口与实现分开。

  4.(*(void(*) ())0)()——这是什么?

  是不是感觉上面的例子太简单,不够刺激?好,那就来点刺激的,看下面这个例子:

  1(*(void(*) ())0)();

  这是《C Traps and Pitfalls》这本经典的书中的一个例子。没有发狂吧?下面我们就来分析分析:

  第一步:void(*) (),可以明白这是一个函数指针类型。这个函数没有参数,没有返回值。

  第二步:(void(*) ())0,这是将0强制转换为函数指针类型,0是一个地址,也就是说一个函数存在首地址为0的一段区域内。

  第三步:(*(void(*) ())0),这是取0地址开始的一段内存里面的内容,其内容就是保存在首地址为0的一段区域内的函数。

  第四步:(*(void(*) ())0)(),这是函数调用。

  好像还是很简单是吧,上面的例子再改写改写:

  1(*(char**(*) (char **,char **))0) ( char **,char **);

  如果没有上面的分析,肯怕不容易把这个表达式看明白吧。不过现在应该是很简单的一件事了。读者以为呢?

  5.函数指针数组

  现在我们清楚表达式

  1char * (*pf)(char * p);

  定义的是一个函数指针pf。既然pf是一个指针,那就可以储存在一个数组里。把上式修改一下:

  1char * (*pf[3])(char * p);

  这是定义一个函数指针数组。

  它是一个数组,数组名为pf,数组内存储了3个指向函数的指针。这些指针指向一些返回值类型为指向字符的指针、参数为一个指向字符的指针的函数。

  这念起来似乎有点拗口。不过不要紧,关键是你明白这是一个指针数组,是数组。函数指针数组怎么使用呢?这里也给出一个非常简单的例子,只要真正掌握了使用方法,再复杂的问题都可以应对。

  如下:

  #include <stdio.h>

  #include <string.h>

  <br>char * fun1(char * p)

  {

  printf("%s\n",p);

  return p;

  }

  char * fun2(char * p)

  {

  printf("%s\n",p);

  return p;

  }

  char * fun3(char * p)

  {

  printf("%s\n",p);

  return p;

  }

  <br>int main()

  {

  char * (*pf[3])(char * p);

  pf[0] = fun1; //可以直接用函数名

  pf[1] = &fun2; //可以用函数名加上取地址符

  pf[2] = &fun3;<br>

  pf[0]("fun1");

  pf[0]("fun2");

  pf[0]("fun3");

  return 0;

  }

  6.函数指针数组的指针

  看着这个标题没发狂吧?函数指针就够一般初学者折腾了,函数指针数组就更加麻烦,现在的函数指针数组指针就更难理解了。

  其实,没这么复杂。前面详细讨论过数组指针的问题,这里的函数指针数组指针不就是一个指针嘛。只不过这个指针指向一个数组,这个数组里面存的都是指向函数的指针。仅此而已。

  下面就定义一个简单的函数指针数组指针:

  1char * (*(*pf)[3])(char * p);

  注意,这里的pf和上一节的pf就完全是两码事了。上一节的pf并非指针,而是一个数组名;这里的pf确实是实实在在的指针。这个指针指向一个包含了3个元素的数组;这个数字里面存的是指向函数的指针;这些指针指向一些返回值类型为指向字符的指针、参数为一个指向字符的指针的函数。

  这比上一节的函数指针数组更拗口。其实你不用管这么多,明白这是一个指针就ok了。其用法与前面讲的数组指针没有差别。下面列一个简单的例子:

  #include <stdio.h>

  #include <string.h>

  char * fun1(char * p)

  {

  printf("%s\n",p);

  return p;

  }

  char * fun2(char * p)

  {

  printf("%s\n",p);

  return p;

  }

  char * fun3(char * p)

  {

  printf("%s\n",p);

  return p;

  }

  int main()

  {

  char * (*a[3])(char * p);

  char * (*(*pf)[3])(char * p);

  pf = &a;

  a[0] = fun1;

  a[1] = &fun2;

  a[2] = &fun3;

  pf[0][0]("fun1");

  pf[0][1]("fun2");

  pf[0][2]("fun3");

  return 0;

  }

  如果你也想成为一名程序员那就我与我交流,不管是零基础还是入门小白学习,要有个相互监督的伙伴,工作需要学习C/C++或者感兴趣、为了入行、转行学习C/C++的伙伴可以一起学习!小编的专栏,手把手教你如何实现!程序猿

激活谷谷主为您准备了激活教程,为节约您的时间请移步至置顶文章:https://sigusoft.com/99576.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/95364.html

(0)
上一篇 2024年 5月 25日 上午7:02
下一篇 2024年 5月 25日 上午7:10

相关推荐

  • 遍历arraylist移除元素

    遍历arraylist移除元素简介我们在项目开发过程中,经常会有需求需要删除ArrayList中的某个元素,而使用不正确的删除方式,就有可能抛出异常。以及在面试中,经常会遇到面试官询问ArrayList遍历时如何正确删除元素。所以在本篇文章中,我们会对几种删除元

    激活谷笔记 2024年 5月 19日
  • tomcat9.0与jdk匹配_tomcat9支持的jdk版本

    tomcat9.0与jdk匹配_tomcat9支持的jdk版本Apache Tomcat 9The internal logging for Apache Tomcat uses JULI, a packaged renamed fork of Apache Commons Logging that is

    激活谷笔记 2024年 5月 15日
  • redis面试题缓存击穿_redis底层实现原理面试题

    redis面试题缓存击穿_redis底层实现原理面试题什么是缓存穿透、击穿、雪崩?缓存常见问题缓存穿透缓存穿透是指查询一个不存在的数据,由于缓存是不命中时被动写的,如果从DB查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到DB去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了。怎么解决?

    2024年 5月 9日
  • html制作注册页面代码包括密码,确认密码_html制作注册页面代码包括密码,确认密码吗

    html制作注册页面代码包括密码,确认密码_html制作注册页面代码包括密码,确认密码吗一、执业药师职业资格考试常见问题执业药师资格考试及注册常见问题解答(2022年3月更新版) 转载于国家药品监督管理局执业药师资格认证中心 一、执业药师职业资格考试常见问题1.执业药师资格考试的报名条件?根据《执业药师

    激活谷笔记 2024年 5月 24日
  • xshell7 激活_Xshell 7使用方法

    xshell7 激活_Xshell 7使用方法xshell7安装激活(永久有效)一、 安装1 卸载之前安装的xshell, 未安装忽略此步骤2 解压本地文件,双击运行xshell**.exe, 按照提示安装等候引导完成3 点击下一步4接受下一步5 选择安装的路径 改成你自己的安装路径6程序文件夹选

    2024年 5月 13日
  • stm32f1串口dma发送数据_stm32f4串口dma发送数据

    stm32f1串口dma发送数据_stm32f4串口dma发送数据STM32F4无人机实现串口+DMA数据收发更多交流欢迎作者抖音号:81849645041目的了解DMA 的工作原理,通过配置 STM32F407 芯片的DMA,来完成通过 DMA 实现串口数据收发的基础实验。原理基于USAR

    2024年 5月 20日
  • node.js安装失败2908

    node.js安装失败2908Nodejs是现代JavaScript语言产生革命性变化的一个主要框架,它使得JavaScript从一门浏览器语言成为可以在服务器端运行、开发各种各样应用的通用语言。在不同的平台下,Nodejs的安装和配置也各有不同。由于Nodejs版本更迭频繁的特点

    激活谷笔记 2024年 5月 18日
  • anaconda安装好了怎么用spyder_安装好anaconda后怎么打开软件

    anaconda安装好了怎么用spyder_安装好anaconda后怎么打开软件anaconda安装后spyder打不开?跪求大佬一步一步教一下,我一次也没用过,也一点也不懂,是老师让下载并成功运行spyder我安装anaconda之后,碰到了spyder打开闪退的故障。后来看到@冷漠是我的保护层 回答,直觉这个回答是正确的。然后再虚拟机中新建了中文账户,安装anacon

    2024年 5月 15日
  • chili和pepper和spicy_chili和pepper有什么区别

    chili和pepper和spicy_chili和pepper有什么区别英语中的“辣”和“椒”想用英文表达出“辣”的感觉,我们首先要知道“辣椒”的英文。People would use "chilli pepper" in BrE while in AmE it is "chili

    激活谷笔记 2024年 5月 25日
  • ubuntu怎么读取u盘名称_ubuntu无法读取源列表

    ubuntu怎么读取u盘名称_ubuntu无法读取源列表为什么说 zsh 是 shell 中的极品?它相对与 bash,tcsh 等有什么优势呢?如果优势很突出的话可以直接只学 zsh 吗?相比bash,zsh兼容bash,且有更灵活多样的配置、更强大的命令补全等,提供了更好的交互易用性;bash是绝大部分*nix系统的默认shell,是事实上的标

    2024年 5月 9日
  • 一阶低通滤波器系统函数_一阶低通滤波器的系统函数

    一阶低通滤波器系统函数_一阶低通滤波器的系统函数一阶RC滤波器算法研究概述滤波器设计是一个非常重要的事情,在实际的工程项目设计时通常利用数字滤波代替模拟滤波,但是数字滤波有时候需要很多资源也很难达到仅仅需要一个电容和电阻实现的模拟滤波电路。在实际的数字滤波器设计时基本的滤波算

    2024年 5月 28日
  • 周大福ctf au750

    周大福ctf au750于昨晚举行的瑰丽珠宝及翡翠首饰 2017 年春季拍卖会苏富比再次刷新任何钻石及珠宝之世界拍卖纪录周大福珠宝以5亿5,300万港元/7,120万美元投得59.60克拉椭圆形内部无瑕艳彩粉红钻“粉红之星”并命名为“CTF Pink”(周大福粉红钻)较此前艳彩粉红钻世界拍卖纪录高出一倍为亚

    激活谷笔记 2024年 5月 18日
关注微信