一:分析自己写出的排序函数的缺点

1.先写出一个极为简单的排序函数(我们先不关心实现排序的算法好坏,只是实现排序功能)

void bubble_sort(int arr[], int sz)

{

int i = 0;

for (i = 0; i < sz - 1; i++)

{

 //一趟冒泡排序int j = 0;

 for (j = 0; j < sz - 1 - i; j++)

 {

 int temp = 0;

                   if (arr[j] < arr[j + 1])

                   {

temp = arr[j];

                         arr[j] = arr[j + 1];

                         arr[j + 1] = temp;

                    }

               }

}


int main()

{

int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };

int sz = sizeof(arr) / sizeof(arr[0]);

bubble_sort(arr, sz);

int i = 0;

for (i = 0; i < 10; i++)

{

 printf("%d ", arr[i]);

}

return 0;

}


2.分析自己写出的函数

缺陷一:

void bubble_sort(int arr[], int sz)

分析函数参数,不难发现,我们传入的数组元素类型只能为int,一旦我们如下定义结构体数组:

struct stu {

char name[10];

short age;

};


int main()

{

struct stu s[3] = { {"张三",20},{"李四",18},{"王五",28} };

pubble_sort(s,sz);//error

}

该数组名s无法通过buttle_sort函数传参。

缺陷二:

即使我们不考虑函数传参问题,我们不难发现,同样定义上面的结构体,我们无法像整数那样直接比较大小来排序,结构体是一个整体,它肯定不能简单的比较a[j]和a[j+1]的大小,那么它该以怎样的方式来排序呢?

让我们来看看c库函数qsort是如何解决这个问题的。

二:分析qsort函数

从官网找到库函数qsort

void qsort (void* base, size_t num, size_t size,

           int (*compar)(const void*,const void*));

从官方文档的介绍中,我们可以清楚的认识到qsort函数各个参数的意义。

(1)void* base参数

void*base定义了一个无类型的指针,它可以被任何类型的指针赋值。实际上base指针就是我们要传进去的数组名(同时也是数组首元素的地址)。

从这里我们可以明显看出设置为void类型的好处:

在我们写的函数中,参数类型是int*,这意味着我们只能传入int型数组的指针,而设置为void 

后,即使我们要排序的数组元素为浮点数或结构体等等,我们也可以将其传参给void*,然后强制

转化为我们想要转化的类型。

(2)num参数

num参数实际上就是数组中元素的个数,用sizeof(arr)/sizeof(arr[0])即可。

(3)size参数

size是指数组中每个元素所占的字节大小,用sizeof(arr[0])即可。

(4)int (*compar)(const void*,const void*)参数

这是一个函数指针,指向一个compar函数,这个函数就是用来定义如何来比较大小的。这个函数有两个参数,都是const void*类型。实际上,由于每个人要进行排序的数组类型不同,这个函数是由我们自己来定义的。

但是官方对于该函数的返回值进行了规定,当第一个元素小于第二个元素,返回一个<0的数;当第一个元素等于第二个元素,返回0;当大于时,返回一个>0的数。

我们通过compar函数来定义如何比较两个参数的大小,然后调用qsort函数,通过我们给出的比较两个参数大小的方法,对整个数组进行排序。

让我们直接看个例子

三:用qsort函数实现排序

(1)实现整形数组排序

int cmp_int(const void* e1, const void* e2)

{

return *(int*)e1 - *(int*)e2;//强制转化为int*类型,之后解引用,再相减

}


void test1()

{

      int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };

int sz = sizeof(arr) / sizeof(arr[0]);

qsort(arr, sz, sizeof(arr[0]), cmp_int);


int i = 0;

for (i = 0; i < sz; i++)

{

 printf("%d ",arr[i]);

}


}

int main()

{

text1();

return 0;

}

在这里我们自己定义了整形数组的比较大小函数cmp_int

(2)实现浮点型数组排序

int cmp_float(const void* e1, const void* e2)

{

return (int)(*(float*)e1 - *(float*)e2) ;

}


void test2()

{

float arr[] = { 8.0,7.0,6.0,5.0,4.0 };

int sz = sizeof(arr) / sizeof(arr[0]);

qsort(arr, sz, sizeof(arr[0]), cmp_float);

int i = 0;

for (i = 0; i < sz; i++)

{

 printf("%f ", arr[i]);

}

}


int main()

{

test2();

return 0;

}

(3)实现结构体数组排序

1.按age排序:

struct stu {

char name[10];

short age;

};


int cmp_stu_by_age(const void* e1, const void* e2)

{

return ((struct stu*)e1)->age - ((struct stu*)e2)->age;

}



void test3()

{

struct stu arr[3] = { {"张三",20},{"李四",18},{"王五",28} };

int sz = sizeof(arr) / sizeof(arr[0]);

qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);

}

int main()

{

test3();

return 0;

}

通过监视窗口可以清楚看到排序变化:

C语言:初步分析c库快速排序函数qsort的使用_sort函数

C语言:初步分析c库快速排序函数qsort的使用_sort函数_02


2.按名字首字母排序:

struct stu {char name[10];

short age;

};


int cmp_stu_by_name(const void* e1, const void* e2)

{

return strcmp(((struct stu*)e1)->name,((struct stu*)e2)->name);//调用库函数strcmp来比较字符串大小

}



void test4()

{

struct stu arr[3] = { {"张三",20},{"李四",18},{"王五",28} };

int sz = sizeof(arr) / sizeof(arr[0]);

qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);

}

int main()

{

test4();

return 0;

}

通过监视窗口看到数组元素序列变化如下:

C语言:初步分析c库快速排序函数qsort的使用_数组_03

C语言:初步分析c库快速排序函数qsort的使用_i++_04



四:将自己写出的bubble_sort写成qsort函数的形式

(1)bubble_sort函数主体


//冒泡排序,能排序所有类型数组

void bubble_sort(void* arr, int sz, int width, int(*cmp)(const void* e1, const void* e2))

{

int i = 0;

for (i = 0; i < sz - 1; i++)

{

 //一趟冒泡排序


            int j = 0;

      for (j = 0; j < sz - 1 - i; j++)

{

if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)

                  swap((char*)arr + j * width, (char*)arr + (j + 1) * width, width);

 }

}

}

(2)swap交换函数

//交换函数

void swap(char* e1, char* e2, int width)

{

int i = 0;

for (i = 0; i < width; i++)

{

char temp = *e1;

      *e1 = *e2;

            *e2 = temp;

      e1++;

            e2++;

}

}

(3)自定义cmp函数

这个函数我们之前已经写过了,就不再重复

(4)测试函数,测试整形,浮点型和结构体类型

这三个函数也已经写过,不再重复


五:总代码

#include<stdio.h>

#include<string.h>


//交换函数

void swap(char* e1, char* e2, int width)

{

int i = 0;

for (i = 0; i < width; i++)

{

 char temp = *e1;

 *e1 = *e2;

 *e2 = temp;

 e1++;

 e2++;

}

}


//冒泡排序,能排序所有类型数组

void bubble_sort(void* arr, int sz, int width, int(*cmp)(const void* e1, const void* e2))

{

int i = 0;

for (i = 0; i < sz - 1; i++)

{

  //一趟冒泡排序

 int j = 0;

 for (j = 0; j < sz - 1 - i; j++)

 {

if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)

swap((char*)arr + j * width, (char*)arr + (j + 1) * width, width);

 }

}

}



//自定义比较函数

int cmp_int(const void* e1, const void* e2)

{

return *(int*)e1 - *(int*)e2;

}


void test1()

{

int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };

int sz = sizeof(arr) / sizeof(arr[0]);

bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);


int i = 0;

for (i = 0; i < sz; i++)

{

 printf("%d ", arr[i]);

}

}



int cmp_float(const void* e1, const void* e2)

{

return (int)(*(float*)e1 - *(float*)e2);

}


void test2()

{

float arr[] = { 8.0,7.0,6.0,5.0,4.0 };

int sz = sizeof(arr) / sizeof(arr[0]);

bubble_sort(arr, sz, sizeof(arr[0]), cmp_float);

int i = 0;

for (i = 0; i < sz; i++)

{

 printf("%f ", arr[i]);

}

}


struct stu {

char name[10];

short age;

};


int cmp_stu_by_name(const void* e1, const void* e2)

{

return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);

}



void test3()

{

struct stu arr[3] = { {"张三",20},{"李四",18},{"王五",28} };

int sz = sizeof(arr) / sizeof(arr[0]);

bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);

}


int main()

{

test1();

printf("\n");

test2();

printf("\n");

test3();

return 0;

}