• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > C语言中的函数指针基础学习教程

C语言中的函数指针基础学习教程

作者:Dennis Kubes 字体:[增加 减小] 来源:互联网 时间:2017-05-28

Dennis Kubes 通过本文主要向大家介绍了C语言中的函数指针基础学习教程等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

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

A)

char * (*fun1)(char * p1,char * p2);
</div>

B)

char * *fun2(char * p1,char * p2);
</div>

C)

char * fun3(char * p1,char * p2);
</div>

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

C)这很容易,fun3是函数名,p1,p2是参数,其类型为char *型,函数的返回值为char *类型。
B) 也很简单,与C)表达式相比,唯一不同的就是函数的返回值类型为char**,是个二级指针。
A) fun1是函数名吗?回忆一下前面讲解数组指针时的情形。我们说数组指针这么定义或许更清晰:

int (*)[10] p;
</div>

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

char * (*)(char * p1,char * p2) fun1;
</div>

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


函数指针和一个简单的函数

我们从一个非常简单的”Hello World“函数入手,来见识一下怎样创建一个函数指针。

#include <stdio.h>
 
// 函数原型
void sayHello();
 
//函数实现
void sayHello(){
  printf("hello world\n");
}
 
// main函数调用
int main() {
  sayHello();
}
</div>

我们定义了一个名为sayHello的函数,它没有返回值也不接受任何参数。当我们在main函数中调用它的时候,它向屏幕输出出”hello world“。非常简单。接下来,我们改写一下main函数,之前直接调用的sayHello函数,现在改用函数指针来调用它。

int main() {
  void (*sayHelloPtr)() = sayHello;
  (*sayHelloPtr)();
}
</div>

第二行void (*sayHelloPtr)()的语法看起来有些奇怪,我们来一步一步分析。

这里,关键字void的作用是说我们创建了一个函数指针,并让它指向了一个返回void(也就是没有返回值)的函数。
就像其他任何指针都必须有一个名称一样,这里sayHelloPtr被当作这个函数指针的名称。
我们用*符号来表示这是一个指针,这跟声明一个指向整数或者字符的指针没有任何区别。
*sayHelloPtr两端的括号是必须的,否则,上述声明变成void *sayHelloPtr(),*会优先跟void结合,变成了一个返回指向void的指针的普通函数的声明。因此,函数指针声明的时候不要忘记加上括号,这非常关键。
参数列表紧跟在指针名之后,这个例子中由于没有参数,所以是一对空括号()。
将上述要点结合起来,void (*syaHelloPtr)()的意义就非常清楚了,这是一个函数指针,它指向一个不接收参数且没有返回值的函数。
在上面的第二行代码,即void (*sayHelloPtr)() = sayHello;,我们将sayHello这个函数名赋给了我们新建的函数指针。关于函数名的更多细节我们会在下文中讨论,现在暂时可以将其看作一个标签,它代表函数的地址,并且可以赋值给函数指针。这就跟语句int *x = &myint;中我们把myint的地址赋给一个指向整数的指针一样。只是当我们考虑函数的时候,我们不需要加上一个取地址符&。简而言之,函数名就是它的地址。接着看第三行,我们用代码'(*sayHelloPtr)();·‘解引用并调用了函数指针。

在第二行被声明之后,sayHelloPtr作为函数指针的名称,跟其他任何指针没有差别,能够储值和赋值。
我们对sayHelloPtr解引用的方式也与其他任何指针一样,即在指针之前使用解引用符*,也就是代码中的*sayHelloPtr。
同样的,我们需要在其两端加上括号,即(*sayHelloPtr),否则它就不被当做一个函数指针。因此,记得声明和解引用的时候都要在两端加上括号。
括号操作符用于C语言中的函数调用,如果有参数参与,就将其放入括号中。这对于函数指针也是相似的,即代码中的(*sayHelloPtr)()。
这个函数没有返回值,也就没有必要将它赋值给任何变量。单独来说,这个调用跟sayHello()没什么两样。
接下来,我们再对函数稍加修改。你会看到函数指针奇怪的语法,以及用调用普通函数的方法来调用赋值后函数指针的现象。

int main() {
void (*sayHelloPtr)() = sayHello;
sayHelloPtr();
}
</div>

跟之前一样,我们将sayHello函数赋给函数指针。但是这一次,我们用调用普通函数的方法调用了它。稍后讨论函数名的时候我会解释这一现象,现在只需要知道(*syaHelloPtr)()和syaHelloPtr()是相同的即可。

带参数的函数指针

好了,这一次我们来创建一个新的函数指针吧。它指向的函数仍然不返回任何值,但有了参数。

#include <stdio.h>
 
//函数原型
void subtractAndPrint(int x, int y);
 
//函数实现
void subtractAndPrint(int x, int y) {
  int z = x - y;
  printf("Simon says, the answer is: %d\n", z);
}
 
//main函数调用
int main() {
  void (*sapPtr)(int, int) = subtractAndPrint;
  (*sapPtr)(10, 2);
  sapPtr(10, 2);
}
</div>

跟之前一样,代码包括函数原型,函数实现和在main函数中通过函数指针执行的语句。原型和实现中的特征标变了,之前的sayHello函数不接受任何参数,而这次的函数subtractAndPrint接受两个int作为参数。它将两个参数做一次减法,然后输出到屏幕上。

在第14行,我们通过'(*sapPtr)(int, int)'创建了sapPtr这个函数指针,与之前的区别仅仅是用(int, int)代替了原来的空括号。而这与新函数的特征标相符。
在第15行,解引用和执行函数的方式与之前完全相同,只是在括号中加入了两个参数,变成了(10, 2)。
在第16行,我们用调用普通函数的方法调用了函数指针。


带参数且有返回值的函数指针

这一次,我们把subtractAndPrint函数改成一个名为subtract的函数,让它把原本输出到屏幕上的结果作为返回值。

#include <stdio.h>
 
// 函数原型
int subtract(int x, int y);
 
// 函数实现
int subtract(int x, int y) {
  return x - y;
}
 
// main函数调用
int main() {
 int (*subtractPtr)(int, int) = subtract;
 
 int y = (*subtractPtr)(10, 2);
 printf("Subtract gives: %d\n", y);
 
 int z = subtractPtr(10, 2);
 printf("Subtract gives: %d\n", z);
}
</div>

这与subtractAndPrint函数非常相似,只是subtract函数返回了一个整数而已,特征标也理所当然的不一样了。

在第13行,我们通过int (*subtractPtr)(int, int)创建了subtractPtr这个函数指针。与上一个例子的区别只是把void换成了int来表示返回值。而这与subtract函数的特征标相符。
在在第15行,解引用和执行这个函数指针,除了将返回值赋值给了y以外,与调用subtractAndPrint没有任何区别。
在第16行,我们向屏幕输出了返回值。
18到19行,我们用调用普通函数的方法调用了函数指针,并且输出了结果。
这跟之前没什么两样,我们只是加上了返回值而已。接下来我们看看另一个稍微复杂点儿的例子——把函数指针作为参数传递给另一个函数。

把函数指针作为参数来传递

我们已经了解过了函数指针声明和执行的各种情况,不论它是否带参数,或者是否有返回值。接下来我们利用一个函数指针来根据不同的输入执行不同的函数。

#include <stdio.h>
 
// 函数原型
int add(int x, int y);
int subtract(int x, int y);
int domath(int (*mathop)(int, int), int x, int y);
 
// 加法 x+ y
int add(int x, init y) {
  return x + y;
}
 
// 减法 x - y
int subtract(int x, int y) {
  return x - y;
}
 
// 根据输入执行函数指针
int domath(int (*mathop)(int, int), int x, int y) {
  return (*mathop)(x, y);
}
 
// main函数调用
int main() {
 
// 用加法调用domath
int a = domath(add, 10, 2);
printf("Add gives: %d\n", a);
 
// 用减法调用domath
int b = domath(subtract, 10, 2);
printf("Subtract gives: %d\n", b);
}
</div>

我们来一步一步分析。

我们有两个特征标相同的函数,add和subtract,它们都返回一个整数并接受两个整数作为参数。
在第六行,我们定义了函数int domath(int (*mathop)(int, int), int x, int y)。它第一个参数int (*mathop)(int, int)是一个函数指针,指向返回一个整数并接受两个整数作为参数的函数。这就是我们之前见过的语法,没有任何不同。它的后两个整数参数则作为简单的输入。因此,这是一个接受一个函数指针和两个整数作为参数的函数。
19到21行,domath函数将自己的后两个整数参数传递给函

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

相关文章

  • 2017-05-28深入理解C++的多态性
  • 2017-05-28c++ 构造函数的初始化列表
  • 2017-05-28详解C++编程中数组的基本用法
  • 2017-05-28简单实现C++复数计算器
  • 2017-05-28使用C语言实现最小生成树求解的简单方法
  • 2017-05-28C++不使用变量求字符串长度strlen函数的实现方法
  • 2017-05-28VC++时钟函数
  • 2017-05-28C++基础入门教程(五):new和delete
  • 2022-04-30在屏幕上输出各种类型的数据
  • 2017-05-28C++ 中使用lambda代替 unique_ptr 的Deleter的方法

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • java 中ArrayList与LinkedList性能比较
    • C++中的extern声明变量详解
    • C++中string的模拟实现
    • VC枚举串口端口应用
    • C++中头文件的概念与基本编写方法
    • Cocos2d-x UI开发之CCControlPotentiometer控件类使用实例
    • C++ 中实现把EXCEL的数据导入数据库(ACCESS、MSSQL等)实例代码
    • C++实现二叉树非递归遍历方法实例总结
    • 学习C语言要掌握的几个库
    • 深入Windows下的回车是回车换行(\r\n)还是换行回车(\n\r)的详解

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有