**C中函数指针的作用 **

项目中用到了很多封装在struct中的函数指针,以前在MFC里面经常用到则个作为回调函数,还以为是微软设计的特色呢。在网上查了一下它的用法,做个总结。

::1)**提供调用的灵活性**。设计好了一个函数框架,但是设计初期并不知道自己的函数会被如何使用。比如C的”stdlib”中声明的qsort函数,用来对数值进行排序。显然,顺序还是降序,元素谁大谁小这些问题,库程序员在编写qsort的时候不可能决定。这些问题是要在用户调用这个函数的时候才能够决定。那边qsort如何保证通用性和灵活性呢?采用的办法是让函数的使用者来制定排序规则。于是调用者应该自己设计comparator函数,传给qsort函数。这就在程序设计初期保证了灵活性。尽管使用函数指针使得程序有些难懂,但是这样的牺牲还是值得的。

2)**提供封装性能**。有点面向对象编程的特点。比如设计一个栈结构

1
2
3
4
5
6
7
8
9
typedef struct _c_stack{ 
int base_size;
int point;
int * base;
int size;
int (*pop)(struct _c_stack *);
int (*push)(int,struct _c_stack *);
int (*get_top)(struct _c_stack);
}c_stack;

在初始化完之后,用户调用这个结构体上的pop函数,只需要s.pop(&s)即可。即使这个时候,工程内部有另外一个函数名字也叫pop,他们之间是不会发生名字上的冲突的。

原因很简单,因为结构体中的函数指针指向的函数名字可能是

1
int ugly_stupid_no_one_will_use_this_name_pop(c_stack *) 

,只是stack的用户是不知道他在调用s.pop(&s),实际上起作用的是这样一个有着冗长名字的函数。

函数指针这种避免命名冲突上的额外好处对于一些库函数的编写者是很有意义的,因为库可能被很多的用户在许多不同的环境下使用,这样就能有效的避免冲突而保证库的可用性。

一般的时候用不到,主要还是一个简化结构和程序通用性的问题,也是实现面向对象编程的一种途径。简单的总结为:

  1. 实现面向对象编程中的多态性

  2. 回调函数

举两个例子:

  1. 你现在要实现一个文件系统,但是文件的介质有硬盘和软盘,那么它们各自的读写函数实现肯定是不一样的。
    好比

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int a_write(char *data_stream, int LBA);
    int b_write(char *data_stream, int LBA);
    有一个结构体维护:
    typedef int (*write_operation)(char* data, int LBA);
    struct {
    write_operation op;
    ...
    } file_struct;
    最后有一个写函数:
    int file_wirte(char *data_stream, int LBA)
    {
    return file_struct.op(data_stream, LBA);
    }
  1. Windows编程中的事件handle函数,即回调函数,在事件队列都是一个函数指针来保存的:

    1
    2
    3
    4
    5
    typedef void (*event_handler) (unsigned int para1, unsigned int para2);
    struct event {
    unsigned int ev_id;
    event_handler handler;
    };struct event event_queue[MAX_EVENT_SIZE];

    程序可以通过扫描这个事件队列来获取每个事件对应的处理函数,然后调用它,即为回调函数。

    3.库函数中函数指针的应用实例:

    快排函数:

    1
    2
    3
    4
    5
    6
    void qsort(void *base, int nelem, unsigned int width, int (*pfCompare)(const void*, const void *));
    /*base = 数组首地址
    *nelem = 数组元素个数
    *width = 元素尺寸(数据类型大小)sizeof(int/double)
    pfCompare = 排序函数地址;
    */
**`作用:对任意类型数组进行排序。`**

# **函数指针的写法**

## C语言qsort的实现(简易版)

1.使用函数指针的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//
// cq_sort.c
// c_lib_realize
//
// Created by echo on 2020/10/12.
// Copyright © 2020 echo. All rights reserved.
//

#include "cq_sort.h"
#include "stdbool.h"
bool Compare(const void * num_1, const void * num_2){
return (int*)num_1 > (int*)num_2 ? true : false;
}

void cq_sort(int *base, int eleNum,int witdh,bool (*pfCompare)(const void*,const void *)){
int tmp = 0,num = 0;
for(int i = 0; i < eleNum; i++){
for(int j = i; j < eleNum; j++){
if(pfCompare(base[i], base[j])){
tmp = base[j];
base[j] = base[i];
base[i] = tmp;
}
}
}
}

void test_cqsort(){
//bool (*pfCompare) (const void*,const void*);
//pfCompare = Compare;
int arr[] = {88,99,44,25,91};
cq_sort(arr, 5, 4, Compare);
for(int i = 0; i < 5; i++)
printf("arr[%d] = %d\n",i,arr[i]);
}


2.直接使用函数名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//
// cq_sort.c
// c_lib_realize
//
// Created by echo on 2020/10/12.
// Copyright © 2020 echo. All rights reserved.
//

#include "cq_sort.h"
#include "stdbool.h"
bool Compare(const void * num_1, const void * num_2){
return (int*)num_1 > (int*)num_2 ? true : false;
}

void cq_sort(int *base, int eleNum,int witdh,bool (*pfCompare)(const void*,const void *)){
int tmp = 0,num = 0;
for(int i = 0; i < eleNum; i++){
for(int j = i; j < eleNum; j++){
if(pfCompare(base[i], base[j])){
tmp = base[j];
base[j] = base[i];
base[i] = tmp;
}
}
}
}

void test_cqsort(){
bool (*pfCompare) (const void*,const void*);
pfCompare = Compare;
int arr[] = {88,99,44,25,91};
cq_sort(arr, 5, 4, pfCompare);
for(int i = 0; i < 5; i++)
printf("arr[%d] = %d\n",i,arr[i]);
}