C語言qsort函數總結


  前幾天在leetcode上刷題,用qsort對二維數組進行排序,發現不會寫qsort的比較函數。后面在網上找了幾篇博客才弄明白,趁今天有空,對這個做一下總結,主要是以下4個方面:

1、qsort總體介紹

2、qsort應用於一維數組

3、qsort應用於指針數組

4、qsort應用於二維數組

 

1、qsort總體介紹

  函數聲明:void qsort(void *base, size_t nitems, size_t size, int (*compare)(const void *, const void*))

  參數解釋:

    base-- 指向要排序的數組的第一個元素的指針。注意這里說是數組,所以必須是對連續的內存塊進行排序。

    nitems-- 由 base 指向的數組中元素的個數

    size-- 數組中每個元素的大小,以字節為單位

    compare-- 用來比較兩個元素的函數。這是qsort最難的一部分了。這里主要要注意以下2點:1、在寫compare函數時,你的兩個形參必須是const void *型,但是在compare函數內部你又必須將const void *類型的形參轉換為實際的類型。這是我最當時最難以理解的一個問題了:我需要轉換成什么類型。看了別人的總結才知道,是:指向數組元素的指針,指向數組元素的指針,指向數組元素的指針。重要的事情說三遍。具體怎么理解,會在下面的例題中詳細講,我認為這時理解qsort的一個綱領。2、comapre函數的返回值是int,即整型。記住這一點,當你在比較double類型時就不會出錯了。如果返回的是一個正數。說明第一個參數排在第二個參數的后面,如果是一個負數,說明第一個參數排在第二個參數的前面。如果等於零,說明它們兩是相等的,誰先誰后都沒關系。

 

 

2、qsort應用於一維數組 

2.1、一維整型數組的用法

  舉例: 

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 int compare(const void *p1, const void *p2) {
 5 
 6     return (*(int *)p1) - (*(int *)p2); // 升序排序
 7     //return (*(int *)p2) - (*(int *)p1); // 降序排序
 8 }
 9 
10 int main(int argc, char** argv)
11 {
12     int i;
13     int arr[5] = { 13, 17, 2, 7, 71 };
14     printf("Before qsort: ");
15     for (i = 0; i < 5; i++) {
16         printf("%d ", arr[i]);
17     }
18     printf("\n");
19 
20     qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(arr[0]), compare);
21     printf("After qsort: ");
22     for (i = 0; i < 5; i++) {
23         printf("%d ", arr[i]);
24     }
25     printf("\n");
26     return 0;
27 }
View Code

  運行結果:

  解釋一下 qsort的每一個參數。qsort第一個參數是指向待排序的數組的第一個元素的指針。由於數組名作為函數的參數時,那么數組名立刻就會轉化為指向該數組第一個元素的指針。因此,第一個參數我們寫的是數組名arr。第二個參數是數組元素的個數,所以可以寫成:sizeof(arr)/sizeof(arr[0])。第三個參數是每個元素的大小,所以可以寫成 sizeof(arr[0])。我們重點來看第四個參數,即compare函數。首先compare函數的入參都寫成了const void*。其次,在函數中,我們需要把入參p1,p2轉換成實際的類型。什么類型? 依據綱領:指向數組元素的指針。我們的數組元素是什么?一個int(整型)。那么指向int的指針自然就是int*了。所以p1,p2在compare函數內部轉換為了 (int *)p1, (int *)p2。然后再取解引用對int進行比較。

  

2.2、一維double型數組的用法

  舉例:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #define ARRAY_LEN 6
 4 
 5 int compare1(const void *p1, const void *p2) {
 6     return (*(double *)p1) - (*(double *)p2);
 7 }
 8 
 9 int main(int argc, char** argv)
10 {
11     int i;
12     double arr1[ARRAY_LEN] = {1.2, -1.2, 1.3, 33.3, 44.5, 77.5};
13     printf("Before qsort: ");
14     for (i = 0; i < ARRAY_LEN; i++) {
15         printf("%.1f ", arr1[i]);
16     }
17     printf("\n");
18 
19     qsort(arr1, sizeof(arr1)/sizeof(arr1[0]), sizeof(arr1[0]), compare1);
20     printf("After qsort: ");
21     for (i = 0; i < ARRAY_LEN; i++) {
22         printf("%.1f ", arr1[i]);
23     }
24     printf("\n");
25     return 0;
26 }
View Code

  我以為這個程序會正確排序,但結果卻啪啪打臉:

   what? 1.3 比 1.2要小?我讀書雖然少,但1.3比1.2大這個常識,我還是知道的。那到底哪里出錯了?沒錯,就是比較函數compare1的返回值寫錯了。比較函數的返回值類型是啥? 整型。int型。但是你看compare1中返回值都寫的是啥?我返回的是兩個double類型相減的類型,還是double型。這樣返回的double類型就會截斷(假如我返回的是 -0.1,截斷后返回的就是0了,就是說1.2和1.3它們誰先誰后都沒關系,那我當然是啥都不做方便啊,自然1.2就放前面了)。返回的結果就不確定了,導致出現這個錯誤。

  下面是修改之后的程序:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #define ARRAY_LEN 6
 4 
 5 int compare1(const void *p1, const void *p2) {
 6     return ((*(double *)p1) > (*(double *)p2)) ? 1 : -1;
 7 }
 8 
 9 int main(int argc, char** argv)
10 {
11     int i;
12     double arr1[ARRAY_LEN] = {1.2, -1.2, 1.3, 33.3, 44.5, 77.5};
13     printf("Before qsort: ");
14     for (i = 0; i < ARRAY_LEN; i++) {
15         printf("%.1f ", arr1[i]);
16     }
17     printf("\n");
18 
19     qsort(arr1, sizeof(arr1)/sizeof(arr1[0]), sizeof(arr1[0]), compare1);
20     printf("After qsort: ");
21     for (i = 0; i < ARRAY_LEN; i++) {
22         printf("%.1f ", arr1[i]);
23     }
24     printf("\n");
25     return 0;
26 }
View Code

運行結果:

 

 

2.3、一維字符數組

  這個沒啥好說的,和一維整型數組的用法類似。直接看一個例題吧:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 
 5 int compare2(const void *p1, const void *p2) {
 6     return (*(char *)p1) - (*(char *)p2);
 7 }
 8 
 9 int main(int argc, char** argv)
10 {
11     char c[] = {'c', 'd', 'a', 'A', 'f', 'B', 'w', 'v', 'W', 'V'};
12     printf("Before qsort: ");
13     for (int i = 0; i < sizeof(c) / sizeof(c[0]); i++) {
14         printf("%c ", c[i]);
15     }
16     printf("\n");
17 
18     qsort(c, sizeof(c) / sizeof(c[0]), sizeof(c[0]), compare2);
19     printf("After qsort: ");
20     for (int i = 0; i < sizeof(c) / sizeof(c[0]); i++) {
21         printf("%c ", c[i]);
22     }
23     printf("\n");
24     return 0;
25 }
View Code

  運行結果:

 

 

3、qsort應用於指針數組

  例題:

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 
 5 int compare(const void *arg1, const void *arg2) {
 6     char *a = *(char**)arg1;
 7     char *b = *(char**)arg2;
 8     return strcmp(a, b);
 9 }
10 
11 int main()
12 {
13     int i;
14     char *arr[5] = { "i", "love", "c", "programming", "language" };
15 
16     qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(char *), compare);
17     for (i = 0; i < 5; i++) {
18         printf("%s ", arr[i]);
19     }
20     printf("\n");
21     return 0;
22 }
View Code

  我們還是來分析一下為什么arg1的類型是 char **。根據綱領:指向數組元素的指針我們首先需要確定數組元素的類型。這里數組元素的類型是是 char *。所以,arg1的類型就是指向char*的指針,即char**。

  運行結果:

 

 

 

4、qsort應用於二維數組

  例題:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 
 5 int cmp1(const void *arg1, const void *arg2)
 6 {
 7     return strcmp((*((char (*)[])arg1)), (*((char (*)[])arg2)));
 8 }
 9 
10 int cmp2(const void *arg1, const void *arg2)
11 {
12     return strcmp((char *)arg1, (char *)arg2);
13 }
14 
15 int main(int argc, char **argv)
16 {
17     int i;
18     char str1[4][8] = {"java", "c", "python", "peal"};
19     printf("COMPARE-FUNCTION-1: ");
20     qsort(str1, 4, sizeof(str1[0]), cmp1);
21     for (int i = 0; i < 4; i++) {
22         printf("%s ", str1[i]);
23     }
24     printf("\n");
25 
26     char str2[4][8] = {"java", "c", "python", "peal"};
27     printf("COMPARE-FUNCTION-2: ");
28     qsort(str2, 4, sizeof(str2[0]), cmp2);
29     for (int i = 0; i < 4; i++) {
30         printf("%s ", str2[i]);
31     }
32     printf("\n");
33     return 0;
34  }
View Code

  這一段代碼剛開始我也是挺疑惑的,為什么compare函數的入參是這個類型的。其實只要記住qsort的綱領:指向數組元素的指針就很容易理解了。首先來看cmp1,我們待排序的是一個二維數組,也就是數組的數組,那么我們這個數組元素的類型就是一個數組類型了(char []類型)。cmp1的參數就是指向一個數組類型的指針了。指向一個char []數組類型的指針當然就是用char (*)[]表示了。

  接下來分析cmp2。我們將str2傳入qsort函數,qsort函數將str2理解為指向數組第一個元素的指針,str2的第一個元素是str2[0][0],所以參數arg1和arg2指的是指向"a[i][0]"的指針,我們知道,a[i][0]是字符,就是char,所以arg1和arg2指的是char *。

  運行結果:

 

參考文獻:

http://www.cppblog.com/Joe/archive/2010/10/29/131746.aspx?opt=admin

https://www.cnblogs.com/nipan/p/4119714.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM