qsort 函数的使用——对普通数组、指针数组、二维数组中的元素进行排序
在ANSI C中,qsort函数的原型是
#include <stdlib.h>
void qsort(void *base, size_t nmemb, size_t size, int (*compar) (const void *, const void *));
解释:qsort函数对含有nmemb个元素的数组进行排序,而base指针指向数组的第一个元素。这个数组的元素个数由size指定。
compar函数对qsort的比较操作进行定义,所以可以定制数字的比较,字符串的比较,甚至结构体的比较,等等。compar的写法见下面几个例子。
一、使用qsort函数对普通数组进行排序。
代码如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int compare(const void *arg1, const void *arg2); 5 6 int 7 main(int argc, char** argv) 8 { 9 int i; 10 11 int arr[5] = { 13, 17, 2, 7, 71 }; 12 13 qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(int), compare); 14 15 for (i = 0; i < 5; i++) { 16 printf("%d ", arr[i]); 17 } 18 printf("\n"); 19 } 20 21 22 int compare(const void *arg1, const void *arg2) { 23 int a = *(int*)arg1; 24 int b = *(int*)arg2; 25 if (a > b) { 26 return 1; 27 } 28 else if (a < b) { 29 return -1; 30 } 31 else { 32 return 0; 33 } 34 }
讲一下参数compar的定义,由于qsort要求传入一个函数指针,所以这个函数的名字我们可以自己设置,就跟其他形参的名字一样,一般用cmp或者compare。
qsort函数把我们传入的arr当做指向数组第一个元素的指针,所以相应地,在代码第22行,形参表中的arg1和arg2,就是指向数组某个元素的指针。
在第23行和第24行,我们将void*转换为int*,并且将指针解引用之后的值赋予a和b,进行比较。
如果a>b返回1,a<b返回-1,就意味着我们进行元素的升序排序,将元素从小到大进行排序。
二、使用qsort函数对指针数组进行排序。
代码如下:
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 int compare(const void *arg1, const void *arg2); 6 7 int 8 main(int argc, char** argv) 9 { 10 int i; 11 12 char *arr[5] = { "i", "love", "c", "programming", "language" }; 13 14 qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(char *), compare); 15 16 for (i = 0; i < 5; i++) { 17 printf("%s ", arr[i]); 18 } 19 printf("\n"); 20 /* getchar(); */ 21 } 22 23 int compare(const void *arg1, const void *arg2) { 24 char *a = *(char**)arg1; 25 char *b = *(char**)arg2; 26 int result = strcmp(a, b); 27 if (result > 0) { 28 return 1; 29 } 30 else if (result < 0) { 31 return -1; 32 } 33 else { 34 return 0; 35 } 36 }
与平常我们所用的数组相比,指针数组有些难理解,其实第12行的arr数组,它就是存放着5个指针,第1个元素存放指向常量字符串"i"的地址,第2个元素存放指向常量字符串"love"的地址,依此类推。
那么我们向qsort传入arr之后,qsort将arr理解为指向数组中第一个元素的指针,所以第23行形参表中,arg1和arg2其实是指向“指向常量字符串的指针”的指针,是char**。而我们需要传给strcmp这个字符串比较函数的,是“指向字符串的指针”,是char*,所以我们将void*转换为char**,然后解引用,得到char*,赋予a和b。接下来使用strcmp对a和b进行比较。
三、使用qsort函数对指针数组进行排序。
代码如下:
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 int compare(const void *arg1, const void *arg2); 6 7 int 8 main(int argc, char** argv) 9 { 10 int i; 11 12 char arr[5][16] = { "i", "love", "c", "programming", "language" }; 13 14 qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(arr[0]), compare); 15 printf("%s\n", arr[0]); 16 for (i = 0; i < 5; i++) { 17 printf("%s ", arr[i]); 18 } 19 printf("\n"); 20 /* getchar(); */ 21 } 22 23 int compare(const void *arg1, const void *arg2) { 24 char *a = (char*)arg1; 25 char *b = (char*)arg2; 26 int result = strcmp(a, b); 27 if (result > 0) { 28 return 1; 29 } 30 else if (result < 0) { 31 return -1; 32 } 33 else { 34 return 0; 35 } 36 }
二维数组比较常见,但是在qsort函数中要对这个数组中的元素进行排序,理解起来稍微有些难度。不过一步一步分析,思路也就自然了。
这里对二维数组进行排序,其实是对二维数组的第二维中存放的字符串进行排序。所以在第14行对qsort函数的调用中,第二个参数是待排元素的个数(5个),第三个参数是待排元素的大小(16)。
我们将arr传入qsort函数,qsort函数将arr理解为指向数组第一个元素的指针,arr的第一个元素是arr[0][0],所以参数arg1和arg2指的是指向"a[i][0]"的指针,我们知道,a[i][0]是字符,就是char,所以arg1和arg2指的是char *。我们将void*转换为char*,赋予a和b,调用strcmp函数对a和b进行比较。
就是这样。