C语言-函数指针

计算机内存可以视为一个线性数组,每个字节按顺序编号。如果一个变量保存的值是该内存数组的索引(值),这样的变量称为指针

澄清:什么是指针?许多C语言的书中,该概念不一致。如 int *p1;

  • K&R指指针变量。也是我采用的定义,即p1是指针,为int*类型(*表示这是一个指针)。
  • 许多C语言的书,指针是所有指针类型的通称,即泛指指针类型,如同数组概念,是int[]、char[]等等的通称;
  • 谭C:指针是指 指针变量的值即一个内存数组的索引、一个地址。如int i = 10; 他把&i 称为指针。

我们按照K&R,指针是各种指针变量的统称;则函数指针(Function Pointers),保存一个函数在内存中的入口地址。

1.函数名与函数指针的种类

C语言中,函数名是编译后的函数代码在内存中的起始地址;因此通常作为函数指针的值。类比,数组名是一个数组在内存中的起始地址。C语言的内存模型中的四个存储区,函数名位于代码区的二进制的机器代码(.text)部分。

typedef _Bool (*Test)(int);  //函数指针

有函数filter (int x,Test test),Test指定了底层函数filter能够使用的函数的种类,表示为(int)→_Bool 。可以将各种函数名赋值给一个函数指针,前提是这些函数必须具有规定的形参列表和返回值类型,这里称之为函数指针的种类,因为不能够称其为函数的类型,它不是一种数据类型。函数指针,只能够指向特定种类的函数。【(int)→_Bool ,一些书上叫函数签名,但是函数签名又通常指一个函数的返回值类型、函数名和形参列表;或函数名和形参列表,总之,函数签名有函数名在其中,而函数指针的种类,不管你是什么函数名字】

 

2.函数指针的用途

  • 实现回调(callback)和行为参数化。
  • 替代分支结构/多态

3.替代分支结构

现有int的四则运算函数4枚,主函数希望按照某个参数(例如char op)决定调用某个函数。
#include <stdio.h> 
int Plus    (int a, int b) { return a+b; }
int Minus   (int a, int b) { return a-b; }
int Multiply(int a, int b) { return a*b; }
int Divide  (int a, int b) { return a/b; }
/*
*switch-statement:op决定执行哪一种操作
*/
void Switch(int a, int b, char op){
   int result=0;
   switch(op){
      case '+' : result = Plus     (a, b); break;
      case '-' : result = Minus    (a, b); break;
      case '*' : result = Multiply (a, b); break;
      case '/' : result = Divide   (a, b); break;
   }
   printf("Switch: %d %c %d = %d\n",a,op,b,result);
}
int main(void){	
   Switch(2,3,'+');
   return 0;
}

我们可以使用函数指针取代分支结构,首先为方便使用,定义How_op,函数指针的种类是(int, int)→int.

typedef  int (*How_op)( int, int); 

int op(int a, int b, How_op  how_op) {
	return how_op(a, b);    // call using function pointer	
}
int main(void){	
	Switch(2,3,'+');
	int r = op(2, 5,  &plus); printf("%d \n",r); 
	return 0;
}
输出:
Switch: 2 + 3 = 5
7
注: Pelles c没有execinfo.h ,不方便根据函数指针输出函数名,

4.函数指针的相关语法

定义函数指针:
int (* how_op)(int, int) = NULL;   //Define a Function Pointer
typedef  int (*How_op)( int, int); 
void op(int a, int b,int (* how_op)(int, int) ){ //as a param
 
函数指针 How_op的种类: int (*)(int, int)
 
赋值(实参):
how_op = &Minus;  //标准
how_op = Minus;  //简写
 
调用:
int result1 = (*how_op) (2,3);   //标准
int result2 = how_op (2,3);   //简写,容易使人误解pt2Func是一个函数。
int result3 = Plus(2,3);   // 比较函数名调用
int result4 = (***Plus) (2,3);   //如果你稀罕
 

5.回调

回调的原始含义:
回调通常指可以被作为参数传递给其他代码的可执行代码块,或者一个可执行代码的引用
在设计一个基础库函数时——假设int op(int a, int b, How_op  how_op)是某个标准库函数,设计者不知道使用者需要何种操作代码——Plus还是Minus等等,于是以函数指针how_op为占位符,而Plus或Minus作为实参传递给op。
但是,在Java中,基础库如IOp定义的抽象方法op(int,int) 被称为回调接口,IOp的子类型给出的op函数的实现被称为回调(函数)。C语言中——没有多态,回调函数需要作为实参传递, int (*)(int, int)型函数如Plus还是Minus,也即回调(函数)。
其实质,是用户需要为基础库函数提供自己的代码,至于是否需要通过作为实参传递,并非回调函数的本质,Java抽象方法op(int,int)不需要第三个参数。
 
 
如果int op(int a, int b, How_op  how_op)让你觉得不像基础库函数,你可以使用stdlib.h中声明的,常用的qsort函数。
extern void __cdecl qsort(void *, size_t, size_t, __cmpfunc *);
#include <stdlib.h>        //          qsort
#include <time.h>          //          randomize
#include <stdio.h>         //          printf
#define ARRAY_SIZE 10

int CmpFunc(const void* _a, const void* _b){
	const float* a = (const float*) _a;
	const float* b = (const float*) _b;
	return (*a > *b)? 1: 
		(*a == *b)? 0:-1;
}
void QSortDemo(void){
	float field[ARRAY_SIZE];
	time_t t;
	srand((unsigned) time(&t));
	for(int c=0;c<ARRAY_SIZE;c++)             // randomize all elements of the field
		field[c]=rand();
	qsort((void*) field, /*number of items*/ ARRAY_SIZE, /*size of an item*/ sizeof(field[0]),
            /*comparison-function*/ CmpFunc);
	printf("the sorted field are ...\n");
	for(int c=0;c<ARRAY_SIZE;c++)
		printf("element #%d contains %.0f\n", c, field[c]);
	printf("\n");
}
 

6.返回一个函数指针

//Return a Function Pointer 
int (*GetPtr(const char op))(int, int){
	return '+'==op ? &Plus : &Minus;
}
void QSortDemo(void);
int main(void){	
	int (*pt2Func)(int, int) = NULL;
	pt2Func = GetPtr('+');
	int result2 = (*pt2Func) (2,3);  
	printf("result2 = %d\n",result2);
	
	QSortDemo();
	return 0;
}

 
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值