计算机内存可以视为一个线性数组,每个字节按顺序编号。如果一个变量保存的值是该内存数组的索引(值),这样的变量称为指针。
澄清:什么是指针?许多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;
}
输出:
7
4.函数指针的相关语法
5.回调
#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;
}