实例讲解C语言函数指针定义及回调函数应用

一个函数会占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这就和数组名表示数组的首地址概念类似。可以把函数的首地址赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这样的指针就是函数指针,函数指针就是指向函数的指针。通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。

1.函数指针的定义

函数指针将C语言的灵活性和强大功能推向了一个新的高度。作为一种指向函数的指针,它不仅能够提高代码的复用性,还能实现回调、模拟面向对象编程以及设计模式等高级特性。

函数指针的定义形式为:

eg:

int (*p)(int, int);

例如:

typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型

(1)returnType 表示函数返回值类型,pointerNmae 为指针名称,param list 为函数参数列表。

(2)param list参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称,这一点和函数原型非常类似。

(3)注意( )的优先级高于*,第一个括号不能省略,如果写作 returnType *pointerName(param list);就成了函数原型,它表明函数的返回值类型为 returnType *。

2. 函数指针的三种定义方式

(1)先定义出函数的类型,再通过类型定义函数指针变量

(2)先定义出函数指针的类型,再通过指针类型定义函数指针变量

(3)重点:直接定义函数指针变量

函数指针和指针函数的区别:

# 指针函数本质是一个函数,其返回值是一个指针:

# 函数指针本质是一个指针,其指向一个函数:

简单点就是:函数名带括号的就是函数指针,否则就是指针函数。

3.函数指针的应用—回调函数

函数指针变量可以作为某个函数的参数来使用,回调函数就是一个通过函数指针调用的函数。

写一个函数A,并把这个函数A的地址赋值给一个函数指针,然后把这个函数指针当作参数赋给另一个函数B,函数B通过函数指针来调用函数A。这个函数A就是回调函数。为什么要使用回调函数,因为使用回调函数能够实现底层代码的解耦。

实例

functionpointMain.c

Method.c

Method.h

运行结果

4.参考内容

[1]菜鸟教程《C函数指针与回调函数》

https://www.runoob.com/cprogramming/c-fun-pointer-callback.html

[2]CSDN作者极客代码的文章《C语言函数指针全面指南:从入门到精通》,文章链接为

https://blog.csdn.net/suifengme/article/details/141174126

[3]C语言中文学习网

[4]CSDN作者woshizuopie的文章《嵌入式C语言基础知识 — 函数指针&回调函数&结构体指针》,文章链接为:https://blog.csdn.net/woshizuopie/article/details/118608507

本文内容来源于网络,仅供参考学习,如内容、图片有任何版权问题,请联系处理,24小时内删除。

作 者 | 郭志龙

编 辑 | 郭志龙校 对 | 郭志龙

你所不知道的C语言:指针篇(函数指针)

为何可以运行?

  • C99 [ 6.3.2.1 ] A function designator is an expression that has function type

=>> Except when it is the operand of the sizeof operator or the unary & operator, a function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’.

=>> * is Right associative operator

  • C99 [6.5.1 ] It is an lvalue, a function designator, or avoid expression if the unparenthesized expression is, respectively, an lvalue, a function designator, or avoid expression.
  • C99 [6.5.3.2-4] The unary * operator denotes indirection. If the operand points to a function, the result is a function designator

补充说明:C语言标准规定,函数指示符(function designator,即函数名字)既不是左值,也不是右值。

除了作为sizeof或取地址&的操作数,函数指示符在表达式中自动转换为函数指针类型右值。因此通过一个函数指针调用所指的函数,不需要在函数指针前加取值或反引用(*)运算符

  • 程序示例
  • 根据 6.3.2.1

test: void (), 会被转成 function pointer: void (*) ()

fptr is a pointer to function with returning type,

  • 根据 6.5.3.2

(*fptr) is a function designator, a function designator will be converted to pointer.

type of (*fptr): void ()

我们可以利用 gdb 去查看这些数值,搭配 print 指令

test 是一个 function designator ,因为不是搭配 &, sizeof 使用,所以会被转成为 pointer to function

(*fptr) 是一個 function pointer 搭配 * (dereference, indirection) operator 使用,则它的结果会被转成为一个 function designator

所以 (**fptr) 要拆成两个部分: (* ( *fptr) ), 里面的 *fptr 是个 function designator, 但是还是会被转成一个 pointer to function,我们可注记为 fptr。

又,外面又有一个 * operator, ( *fptr’ 是個 function designator, 最后还是会被转化为 pointer to function

但是,0x400526 这个数值是怎么来的呢?

我们可以使用以下指令观察:

参考输出里面的一段:

这个数值其实是可执行文件反编译后的函数入口,但是这个数值并非是在实际内存中的数值,而是虚拟存储器的地址。

由于puts 的 function prototype 是 int puts(const char *s),因此每次经过 * operator 运算后得到的结果是仍然是 int。所以,* 的数目不會影响结果。最后return 的值是根据 s 的长度加上 ’\\n’。而这个例子 return 给 main 的值是 6。

对应到 C99/C11 规范 [ 6.5.3.2 ],& 所能操作的 operand 只能是:

  • function designator – 基本上就是 function name
  • [] or * 的操作结果:跟这两个作用时,基本上就是相消

* – operand 本身

[] – & 会消失,而 [] 会被转换成只剩 + (注:原本 [] 会是 + 搭配 *)

例如: &(a[5]) == a + 5

  • 一个指向非 bit-field or register storage-class specifier 的 object 的 lvalue

bit-field:一种在 struct 或 union 中使用用来节省内存空间的object;特別的用途:沒有名称的 bit-field 可以做为padding

除了遇到 [] 或 * 外,使用 & 的结果基本上都是得到 pointer to the object 或是 function 的 address

考虑以下程序:

为何 str == &str 呢?

  • 实际上左右两边的类型是不一样的,只是值相同。
  • 左边的是 pointer to char:char *

规范中表示:除非遇到 sizeof 或是 & 之外,array of type (在这就是指 str) 都会被直接解释成 pointer to type (在這就是 pointer to char),而这个type 是根根据 array 的第一個元素来

Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. (C99 6.3.2.1)

  • 右边的则是 pointer to an array: char (*)[123]

上面提到:遇到 & 时,str 不会被解读为pointer to type,而是作为原本的 object,在这就是 array object,而 address of array object 也就是这个 array object 的起始地址,当然也就会跟第一个元素的地址相同

除了用值相同來解释外,规范在提到 equality operators 时,也有说到类似情景

Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function (C99 6.5.9)

  • 指针本身不可变更 (Constant pointer to variable): const 在 * 之后

char * const pContent;

  • 指针所指向的內容不可变更 (Pointer to constant): const 在 * 之前

const char * pContent;char const * pContent;

  • 两者都不可变更

const char * const pContent;

  • array argument 的正确使用时机

Are there any cases of multi-dimensional arrays? Because those actually have semantic meaning outside of sizeof(), just in things like adding offsets. Eg something like

  • ends up being equivalent to something like

and \”a+1\” is actually 40 bytes ahead of \”a\”, so it does act like an \”int *\”. (And I might have screwed that up mightily – C multidimensional arrays and the conversions to pointers are really easy to get confused about. Which is why I hope we don’t have them)

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。