周立功教你学程序设计技术:做好软件模块的分层设计,回调函数要这样写
清单 2.5。
程序清单 2.5 compare数据比较函数的实现
1 int compare_int(const void *e1, const void *e2)
2 {
3 return (*((int *)e1) - *((int *)e2)); // 升序比较
4 }
5
6 int compare_int_invert(const void *e1, const void *e2)
7 {
8 return *(int *)e2 - *(int *)e1; // 降序比较
9 }
10
11 int compare_vstrcmp(const void *e1, const void *e2)
12 {
13 return strcmp(*(char**)e1, *(char**)e2); // 字符串比较
14 }
注意,如果e1是很大的正数,而e2是大负数,或者相反,则计算结果可能会溢出。由于这里假设它们都是正整数,从而避免了风险。
由于该函数的参数声明为void *类型,因此数据比较函数不再依赖于具体的数据类型。即可将算法的变化部分独立出来,无论是升序还是降序或字符串比较完全取决于回调函数。注意,之所以不能直接用strcmp()作为字符串的比较,因为bubbleSort()传递的是类型为char **的数组元素的地址&array[i],而不是类型为char*的array[i]。
(3)bubbleSort()冒泡排序函数
标准函数bubbleSort()是C中使用函数指针的经典示例,该函数是对一个具有任意类型的数组进行排序,其中单个元素的大小和要比较的元素的函数都是给定的。其原型初定如下:
bubbleSort(参数列表);
既然bubbleSort()是对数组中的数据排序,那么bubbleSort()必须有一个参数保存数组的起始地址,且还有一个参数保存数组中元素的个数。为了通用还是在数组中存放void *类型的元素,这样一来就可以用数组存储用户传入的任意类型的数据,因此用void *类型参数保存数组的起始地址。其函数原型如下:
bubbleSort(void *base, size_t nmemb);
由于数组的类型是未知的,那么数组中元素的长度也是未知的,同样也需要一个参数来保存。其函数原型进化为:
bubbleSort(void *base, size_t nmemb, size_t size);
其中,size_t是C标准库中预定义的类型,专门用于保存变量的大小。参数base和nmemb标识了这个数组,分别用于保存数组的起始地址和数组中元素的个数,size存储的是打包时单个元素的大小。
此时,如果将指向compare()的指针作为参数传递给bubbleSort(),即可"回调"compare()进行值的比较。由于排序是对数据的操作,因此bubbleSort()没有返回值,其类型为void,bubbleSort()函数接口详见程序清单 2.6。
程序清单 2.6 bubbleSort()冒泡排序函数接口(bubbleSort.h)
1 #pragma once;
2 void bubbleSort(void *base, size_t nmemb, size_t size, COMPARE compare);
虽然大多数初学者也会选择回调函数,但又经常用全局变量保存中间数据。这里提出的解决方法就是给回调函数传递一个称为"回调函数上下文"的参数,其变量名为base。为了能接受任何数据类型,选择void *表示这个上下文。"上下文"的意思就是说,如果传进来的是int类型值,则回调int型数据比较函数;如果传进来的是字符串,则回调字符串比较函数。
当bubbleSort()将base声明为一个void *类型时,即允许bubbleSort()用相同的代码支持不同类型的数据比较实现排序,其关键之处是type类型域,它允许在运行时根据数据的类型调用不同的函数。这种在运行时根据数据的类型将函数体
- 电源软启动的实用设计技巧(07-16)
- 周立功:动态分布内存——malloc()函数与calloc()函数(07-22)
- 周立功“程序设计与数据结构”:深度解剖动态分布内存的free()函数与realloc()函数(07-25)
- 周立功教你学C语言编程:教你数组是如何保存指针的(07-31)
- 算法的泛化问题,这些坑你可能都经历过!|周立功教你学软件设计(08-01)
- 所有C语言数组和指针的知识都在这里了!|周立功手把手教你学C语言编程(08-01)
