C language advanced pointers (3) - implementation of qsort

Hello everyone, today we are going to learn the implementation of the callback function qsort.

Insert image description here
First let us open cplusplus.com and find the qsort function.

Insert image description here
When we see this function, we can see its header file and parameter information.

#include<stdlib.h>
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));

Let's take a look at its specific parameter information:
void* base: the address of the first element of the array to be sorted
size_t num: the number of elements of the array to be sorted
size_t size: the size unit of an element in the array to be sorted is byte
int ( compar)(const void , const void*)): Here is a function pointer compar, which points to a comparison function, which is used to compare two elements. What is placed inside is the address of the two elements to be compared.

If we want to learn this function, we first have to review the bubble sort method. Here you can refer to my previous blog that analyzed the bubble sort method. I will not introduce it to you one by one here. https://editor.csdn.net/md/?articleId=132266676 Visit this website to see the blog I wrote before about the bubble sort method.

Why did we learn the bubble sorting method and still use the qsort sorting function? That is because of the advantages of this function. It can sort both arrays and structures. However, you should pay attention to the following when sorting:

  1. To sort an integer array, two integers can be compared directly using >
  2. To sort the structure array, the data of the two structures may not be directly used > to compare, that is, to compare the sizes of different types of data, the methods are different.

We see the code ->:

#include <stdio.h>
//qosrt函数的使用者得实现一个比较函数
int int_cmp(const void * p1, const void * p2)
{
    
    
return (*( int *)p1 - *(int *) p2);
}
int main()
{
    
    
int arr[] = {
    
     1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
{
    
    
printf( "%d ", arr[i]);
}
printf("\n");
return 0;
}

We define the array arr here, and we want to sort it in ascending order. We only need to reference the qsort function. First, we pass parameters to the qsort function. The first one passed is the address of the first element in the array. The second one is the number of elements in the array. We can calculate it using sizeof. sizeof(arr) calculates the size of the entire array, while sizeof(arr[0]) calculates the size of each element. The two values ​​are similar. In addition, that is the number of elements in this array. Because this is an integer array, our third parameter is to pass the size of each element. Just pass sizeof (int). The last thing passed is the address of a function pointer. , and the address stored by the first pointer in the function pointer is of course the address of the first element of the array, and the second pointer stores the address of the element to be compared with the first one. We all understand it, so let’s take a look Let’s look at the results of running the program.

Insert image description here
We have now learned to use this function, so how do we simulate the use of this function? This is the focus of what we are going to learn today. Today we will share how to use the qsort function to sort integer arrays and structure variables. .

First, let's simulate the implementation of ascending order of an integer array:

void print_arr(int arr[], int sz)
{
    
    
	int i = 0;
	for (i = 0; i < sz; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* e1, const void*e2))
{
    
    
	//冒泡排序的趟数
	int i = 0;
	for (i = 0; i < num - 1; i++)
	{
    
    
		//一趟冒泡排序
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
    
    
			if (arr[j] > arr[j + 1])
			
			{
    
    
				//交换
				int tmp=arr[j];
				arr[j]=arr[j+1];
				arr[j+1]=tmp;
			}
		}
	}
}

void test1()
{
    
    
	int arr[] = {
    
     0,1,2,3,4,5,6,7,8,9 };//升序
	//排序为降序
	int sz = sizeof(arr) / sizeof(arr[0]);
	print_arr(arr, sz);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	print_arr(arr, sz);}
	
int main()
{
    
    
test();
return 0;
}	

What we should pay attention to here is: int ( cmp)(const void e1, const void* e2)
e1 is a pointer, which stores the address of an element to be compared. e2 is a pointer, which stores the address of an element to be compared. e1 The element pointed to > the element pointed to by e2, returns a number > 0 The
element pointed to by e1 == the element pointed to by e2, returns 0 The element pointed to by e1 < the element pointed to by e2, returns a number < 0

We first define the array and call the function test(), which will jump to the function test. In this function, we use sz to represent the number of array elements. The solution method is still the old method of sizeof. We call it into the function Print the original array in print_arr, and then call the function bubble_sort. In it, we use nested loops to perform bubble sorting and print.

So how do we implement the sorting of structures, because
all pointers in our function pointer int (cmp) (const void e1, const void* e2) are void* type, which is equivalent to a trash can, which can be stored The address of any type of variable is inseparable from the ordering of our structures.

Let’s first define a structure variable:

struct Stu
{
    
    
	char name[20];//20
	int age;//4
};
void test2()
{
    
    
	struct Stu arr[] = {
    
     {
    
    "zhangsan", 20}, {
    
    "lisi", 30}, {
    
    "wangwu", 15}};
	int sz = sizeof(arr) / sizeof(arr[0]);//3
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
int main()
{
    
    
	//整型数据/字符数据/结构体数据...
	//可以使用qsort函数对数据进行排序
	//测试bubble_sort,排序整型数据
	//test1();

	//测试bubble_sort,排序结构体的数据
	test2();
	return 0;
}

Seeing the code block, we found that there are two types of variables defined in the structure, one is the name char type, and the other is the age int type. Then there are two ways for us to sort it, one is to sort by name, and the other is to sort it by name. The species are sorted by age.

int cmp_stu_by_age(const void* e1, const void*e2)
{
    
    
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}


int cmp_stu_by_name(const void* e1, const void* e2)
{
    
    
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

It should be noted that the type of the pointer here is void* and needs to be cast. This is the comparison function of our two methods. So how do we perform the exchange? We will see our exchange function

void swap(char* buf1, char* buf2, size_t size)
{
    
    
	int i = 0;
	for (i = 0; i < size; i++)
	{
    
    
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

swap((char*)base + j * size, (char*)base + (j + 1) * size, size)

We see the exchange function here:

(char*)base + j * size: Determine the address of the first data to be exchanged
(char*)base + (j + 1) * size: Determine the address of the second data to be exchanged
size: Determine these two How many bytes are directly separated by the data to facilitate byte-by-byte operations?

Because our base is a char* pointer, it stores the address of the first element in the structure variable, size represents the size of each element, j * size represents how many elements to skip, and between two elements The exchange is the exchange between bytes, so we calculate the size of each element. The char * type represented by the pointer is a byte, so an exchange of two elements is an exchange of one byte. , so we only need to use a loop here to achieve the exchange of two elements.

Next let's take a look at the complete code:

#include <string.h>

void print_arr(int arr[], int sz)
{
    
    
	int i = 0;
	for (i = 0; i < sz; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
	printf("\n");
}

//int (*cmp)(const void* e1, const void* e2)
//e1是一个指针,存放了一个要比较的元素的地址
//e2是一个指针,存放了一个要比较的元素的地址
//e1指向的元素>e2指向的元素,返回>0的数字
//e1指向的元素==e2指向的元素,返回0
//e1指向的元素<e2指向的元素,返回<0的数字
//

void swap(char* buf1, char* buf2, size_t size)
{
    
    
	int i = 0;
	for (i = 0; i < size; i++)
	{
    
    
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}


//泛型编程
void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* e1, const void*e2))
{
    
    
	//冒泡排序的趟数
	int i = 0;
	for (i = 0; i < num - 1; i++)
	{
    
    
		//一趟冒泡排序
		int j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
    
    
			//if (arr[j] > arr[j + 1])
			if(cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0)
			{
    
    
				//交换
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}


int cmp_int(const void*e1, const void*e2)
{
    
    
	return *(int*)e1 - *(int*)e2;
}

void test1()
{
    
    
	int arr[] = {
    
     0,1,2,3,4,5,6,7,8,9 };//升序
	//排序为降序
	int sz = sizeof(arr) / sizeof(arr[0]);
	print_arr(arr, sz);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	print_arr(arr, sz);
}	

struct Stu
{
    
    
	char name[20];//20
	int age;//4
};

int cmp_stu_by_age(const void* e1, const void*e2)
{
    
    
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}


int cmp_stu_by_name(const void* e1, const void* e2)
{
    
    
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

void test2()
{
    
    
	struct Stu arr[] = {
    
     {
    
    "zhangsan", 20}, {
    
    "lisi", 30}, {
    
    "wangwu", 15}};
	int sz = sizeof(arr) / sizeof(arr[0]);//3
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}

int main()
{
    
    
	//整型数据/字符数据/结构体数据...
	//可以使用qsort函数对数据进行排序
	//测试bubble_sort,排序整型数据
	test1();

	//测试bubble_sort,排序结构体的数据
	test2();
	return 0;

Here we only need to call the function test2 to access it. In test2, we upload the required parameters to the function bubble_sort. Here we compare the two elements and call the functions int cmp_stu_by_age and int cmp_stu_by_name. In the loop Compare in and pass parameters to the functions int cmp_stu_by_age(const void* e1, const void e2) and int cmp_stu_by_name(const void e1, const void* e2). If the returned value is greater than 0, pass the parameters to the exchange function for exchange. , if it is less than 0, no exchange is needed, and the next element is compared along the loop.

We know that age is an integer and it is very simple to compare the size. Then our name is of char type. How to compare and sort this? Because our name is a string, comparing the size between names is to compare the strings. The size of the string, so how to compare the size of the string? That is the size of the Ascll value at the corresponding position in the string. If they are equal, jump to the next character for comparison, and so on until the size is compared.

Let us see the following code to verify the string size comparison:

#include<stdio.h>
#include<string.h>
int main()
{
    
    
	char arr1[20] = "abc";
	char arr2[20] = "abe";
	if (strcmp(arr1, arr2) > 0)
		printf(">\n");
	else
		printf("<=\n");

	return 0;
}

Insert image description here
We see that the Ascll values ​​of the first and second characters in the picture are the same size, and the third position e>c, so the printed result is <=.
Insert image description here
So everyone should have a deeper understanding of the implementation of the qsort function. That’s it for today’s sharing. Thank you all.

Guess you like

Origin blog.csdn.net/Lehjy/article/details/132925423