大家好,我是小张同学,今天总结函数指针。
目录
1. 函数指针
1.1 函数指针引入
所谓函数指针就是一个指向函数的指针变量。所以函数指针是一个指针,不是函数,就像int *n = &a,指针变量 n 存放的是 a 的地址,那么函数指针存放的就是一个函数的地址。
更详细点说,如果在程序中定义了一个函数,在编译时系统会为这个函数分配一段存储空间,这段存储空间的首地址就是这个函数的地址,并且这个地址可以用函数名表示,既然是地址,我们就可以定义一个指针变量来存放,这个指针变量就是函数指针。
举个例子:
int function(int x, int y);
int(*pFunction)(int x, int y) = &function; //注意这里 *和pFunction是用括号括起来的
int(*pFunction)(int x, int y) = function; //初始化时,&操作符是可选的,因为函数名本身就是函数的地址
以上代码定义了一个函数指针变量 pFunction,并将其初始化为指向函数 function,首先 (*pFunction)表明这是一个指针变量,第一个 int 表示这个指针可以指向返回值为 int 类型的函数,后面括号中的两个 int 表示这个指针可以指向有两个 int 参数的函数,所以,合起来就是这个指针变量可以指向一个返回值类型为 int 型,且有两个int 型参数的函数。
所以函数指针的定义方式就是 函数返回值类型 (*指针变量名) (函数参数列表)
既然pFunction是一个变量,那么它的类型是什么呢,把变量名去掉,就是它的类型,所以pFunction的类型为 int(*)(int,int)。
指针函数,指的是函数的返回值是一个指针,比如我定义一个函数,这个函数返回的类型是一个 int*,如下:
int *p(int a, int b); //注意这里 *和p之间是没有括号的
1.2 函数指针使用
在函数指针被声明并且初始化之后,我们可以使用三种方式调用函数:
int function(int x, int y) //定义一个函数
{
printf("x = %d, y = %d\n", x, y);
return 0;
}
int (*pFunction)(int x, int y) = function; //定义一个函数指针,并用function函数的地址将其初始化
int x = 1;
int y = 2;
function(x, y);
(*pFunction)(x, y); //通过函数指针调用function函数
pFunction(x, y); //通过函数指针调用function函数
第一种:简单地使用函数名字调用,但其实在执行地过程中,函数名function首先被转换为一个函数指针,然后执行始于这个地址地代码。
第二种:*pFunction把函数指针转换为一个函数名,这个转换实际并不需要,因为编译器在执行过程中又会把它转换回去。
第三种:直接使用pFunction,编译器需要的是一个函数指针。通常使用第三种方式。
2. typedef函数指针
typedef int (*p)(int x, int y);
以上代码的含义是:
p是一个类型别名,也就是将 void(*)(int,int) 这个类型,定义了一个别名p。
举个例子:
typedef int (*p)(int x, int y); //为 int(*)(int, int)类型定义一个别名p
p pFunction; //定义一个p类型的变量pFunction
int function(int x, int y) //定义一个函数
{
printf("x = %d, y = %d\n", x, y);
return 0;
}
void main()
{
pFunction = function; //将function函数的首地址赋给变量pFunction
pFunction(2, 3); //通过函数指针调用函数
}
3. 回调函数
函数指针更重要的意义在于回调函数。
让我们来构造一个场景,希望有一个函数可以在整数链表中查找一个值,如下:
Node* search_list(Node *node, int const value)
{
while(NULL != node)
{
if(node->value == value)
{
break;
}
node = node->next;
}
return node;
}
上面这个函数只适用于 value 值为 int 类型的链表,如果我们需要在一个 value 值为 char 类型的链表中查找,那就需要再写另外一个新函数,新函数和上面的函数绝大部分代码都相同,只是 value 类型不同。
一种更为通用的方法是让查找函数 search_list 与类型无关,这样函数就可以对任何类型的值进行比较,这就需要用到函数指针了。需要编写一个比较函数,然后将这个比较函数的指针作为参数传递给查找函数 search_list,如下:
Node* search_list(Node *node, void const *value, int (*compare)(void const *, void const *))
{
while(NULL != node)
{
if(compare(&node->value, value) == 0)
{
break;
}
node = node->next;
}
return node;
}
我看上面 search_list传入的函数指针不顺眼,来用上刚刚说的 typedef 试试,于是改成了下面这样:
typedef int (*compare)(void const *, void const *);
Node* search_list(Node *node, void const *value, compare pCompare)
{
while(NULL != node)
{
if(pCompare(&node->value, value) == 0)
{
break;
}
node = node->next;
}
return node;
}
如果需要在整数链表中查找,就编写一个比较函数 compare_ints,如下:
int compare_ints(void const *a, void const *b)
{
if(*(int*)a == *(int*)b)
{
return 0;
}
else
{
return 1;
}
}
//注意这里的强制类型转换
这个比较函数就要像下面这样使用,将其地址作为参数传到search_list函数中:
desired_node = search_list(root, &desired_value, compare_ints);
我们上面使用的技巧就是回调函数,回调函数就是将一个函数指针作为参数传递给其他函数,后者将"回调"这个函数。
如果希望在字符串链表中查找,以下代码就可以实现:
desired_node = search_list(root, "desired_value", strcmp);
于是函数指针就基本介绍完了,我们下期接着讲指针