C函数指针

内存可以视为一个线性数组。如果一个变量保存的值是内存数组的索引(值),这样的变量称为指针。【许多C语言的书中,似乎说指针是特殊类型的通称,即指针类型,如同数组是int[]、char[]等等的通称;谭C的指针是指指针变量的值即一个内存数组的索引、一个地址。K&R指指针变量。】

函数指针(Function Pointers),也是一个指针变量,保存一个函数在内存中的入口地址。

1.函数名与函数签名

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

函数签名,指一个函数的返回值类型、函数名和形参列表。【许多Java语言的书中,方法签名的界定并不一致。大多数情况下,指函数名和形参列表,参见《编程导论(Java)》p59】
函数指针,总是指向特定签名的函数。可以将各种函数名赋值给一个函数指针,但是这些函数除函数名外,必须具有相同的返回值类型和形参列表——这些也即函数指针的类型。

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;
}
我们编写Switch函数的替代品:以 函数指针pt2Func为参数
void Switch_With_Function_Pointer(int a, int b, int (*pt2Func)(int, int)){
	int result = pt2Func(a, b);    // call using function pointer	
	printf("Switch_With_Function_Pointer: %p (%d ,%d) = %d\n",pt2Func,a,b,result);
}
int main(void){	
	Switch(2,3,'+');
	Switch_With_Function_Pointer(2, 5,  &Minus);// Pass a Function Pointer as an Argument 
	return 0;
}
输出:
Switch: 2 + 3 = 5
Switch_With_Function_Pointer: 004010e0 (2 ,5) = -3
注: Pelles c没有execinfo.h ,不方便根据函数指针输出函数名,

4.函数指针的相关语法

定义函数指针pt2Func:
int (*pt2Func)(int, int) = NULL;   //Define a Function Pointer
void Switch_With_Function_Pointer(int a, int b, int (*pt2Func)(int, int)){ //as a param
函数指针pt2Func的类型: int (*)(int, int)

赋值(实参):
pt2Func = &Minus;  //标准
pt2Func = Minus;  //简写

调用:
int result1 = (*pt2Func) (2,3);   //标准
int result2 = pt2Func (2,3);   //简写,容易使人误解pt2Func是一个函数。
int result3 = Plus(2,3);   // 比较
int result4 = (***Plus) (2,3);   //如果你稀罕

5.回调

回调的原始含义:
回调通常指可以被作为参数传递给其他代码的可执行代码块,或者一个可执行代码的引用
参考 《编程导论(Java)·9.3.1Java回调-2》
在设计一个基础库函数时——假设void Switch_With_Function_Pointer(int a, int b, int (*pt2Func)(int, int))是某个标准库函数,设计者不知道使用者需要何种操作代码——Plus还是Minus等等,于是以函数指针pt2Func为占位符,而Plus或Minus作为实参传递给 Switch_With_Function_Pointer。
因此,在Java中,基础库如IClient定义的抽象方法callback(int) 被称为回调接口,IClient的子类型如Client给出的callback实现被称为回调(函数)。C语言中——没有多态,作为实参传递的 int (*)(int, int)函数如Plus还是Minus,也即回调(函数)。
其实质,用户为基础库函数提供自己的代码。
如果Switch_With_Function_Pointer让你觉得不像基础库函数,你可以使用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;
}





你可能感兴趣的:(C函数指针)