Playing with the qsort function for sorting - [C language]

Speaking of sorting, we will think of many algorithms, which I have written about in previous blogs, such as: bubble sorting, quick sorting, selection sorting, etc. In fact, in the C language, there has always been a library function that can sort the contents of the array and has complete functions——qsort function. Let's explore today!


Table of contents

Callback

initial qsort function

How to create a comparison function

A reference to the qsort function

Simulate qsort function


Callback

When understanding the qsort function, let's first talk about what the callback function is.

Callback:

A callback function is a function that is called through a function pointer. If you pass a function pointer (address) as a parameter to another function, when this pointer is used to call the function it points to, we say this is a callback function. The callback function is not directly called by the implementer of the function, but is called by another party when a specific event or condition occurs, and is used to respond to the event or condition.

The callback function is actually a name for a certain type of function. When I use it, I feel more like a nested relationship. Using the callback function at the corresponding time will have unexpected gains.

We will use the callback function when using the qsort function, so the qsort function can be used in a wider range of common types (integer, floating point, string, structure, etc.). Next, let us walk into the qsort function!


initial qsort function

The qsort library function is in #include<stdlib.h>, and its function is to sort the elements in the array.

Features of the qsort function:

1. Using the quick sort method

2. Suitable for sorting any type of data

 The above is the return value of the qsort function and the contents of each parameter. Now we analyze:

Return value: void (does not return anything)

parameter:

void* base: Pointer to the first object of the array to be sorted, converted to void *

size_t num: the number of elements pointed to by base in the array. size_t is an unsigned integer.

size_t size: The size of each element in the array (in bytes) size_t is an unsigned integer.

int (*compar) (const void*, const void*): This is a function pointer, the actual parameter should be given the address of a function, and the return value of the given function is int, and the two parameters point to compare two elements pointer to the function.

 

So when using the qsort function, we should create a comparison function and pass it into the qsort function for use. The created comparison function should match the content of the array you want to compare, corresponding to different array content, we should create different comparison functions for comparison.


How to create a comparison function

When creating the compare function that you want to compare, the most important point is to follow the template of the qsort parameter to create, otherwise the qsort function will not be available.

void* (null pointer): It can receive any type of pointer variable, but it cannot perform pointer operations because it has no space access rights. If you do not pay attention to this situation, the compiler will report an error. Therefore, when we assign a value , we must cast it to the data type we need.

When the sorted array is an integer array:

int compar(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}

When the sorted array is an array of strings, we should use the strcmp function in #include<string.h> for comparison:

int compar(const void* p1, const void* p2)
{
	return (strcmp(*(char*)p1 , *(char*)p2));
}

 When the structure array is used, when the comparison content is a string:

int compar(const void* p1, const void* p2)
{
	return strcmp((struct stu*)p1->name , (struct stu*)p2->name);
}

 The above are a few typical examples. After reading these examples, I believe that I already know how the compar function should be created. Let's take a piece of code for practical operation.


A reference to the qsort function

According to the above description, we have basically mastered the basic usage of the qsort function, now let us have a further understanding of the qsort function through the code.

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct Stu
{
	char name[20];
	int age;
};
void print(int arr[10], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

int compar1(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}

int compar2(const void* p1, const void* p2)
{
	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}


void test1()
{
	int arr1[10] = { 3,1,5,2,9,7,4,8,0,6 };
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	qsort(arr1, sz, sizeof(arr1[0]), compar1);
	print(arr1, sz);
}

void test2()
{
	struct Stu arr2[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu",16} };
	int sz = sizeof(arr2) / sizeof(arr2[0]);
	qsort(arr2, sz, sizeof(arr2[0]), compar2);
	for (int i = 0; i < sz; i++)
	{
		printf("\n%s %d", arr2[i].name, arr2[i].age);
	}
}
int main(void)
 {
	test1();
	test2();
	return 0;
}

This code sorts an array of integers and strings in an array of structures in ascending order. Here I want to say that the ratio between characters is compared by their ASCII code value. The running results are as follows:  If we want to sort in descending order, we only need to exchange the two values ​​compared in the compare function.


Simulate qsort function

According to some functional characteristics of the qsort function we are using, the function of the qsort function is simulated.

The function of the qsort function is sorting, so there must be some kind of sorting method in this function, here we will use the bubble sorting method. (Other sorting methods are also possible)

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++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

This is the most standard bubble sorting method. We use the above sorting code to simulate the qsort function for the framework.

If we use this function as the qsort function, we will find many problems! ! !

Question 1: Parameters can only receive integer arrays, other arrays cannot be received.

Solution: We can imitate the first parameter in the qsort function and change the first parameter to void* base, so that we can receive any type of array.

Question 2: When we use void*base as the first parameter, how should the movement of the pointer be realized? (size and number of array elements)

Solution: Continue to follow the qsort function, adding size_t num and size_t size.

Question 3: The comparison of two numbers in the above function is a comparison based on integers, so it can be realized by using > < >= <=. If the incoming array is a structure array, we cannot use the comparison method of comparing integers. (For different types of data, you cannot simply use > to compare)

Solution: We need to use the callback function to pass the comparison method of the two elements in the form of function parameters. (the function pointer will be the fourth argument) 

When the compare function is created, how should we pass parameters to the compare function?

The compare function is multiple functions. We can compare different arrays through different calls, but there is only one place to pass parameters to the compare function in the simulated function. We should consider all array types.

We know the size of each element in the array, which is equivalent to knowing the size of its elements in memory. In order to take into account all data, we use the pointer char* with the least access to memory as the basic unit, and multiply each The size of the element, you can achieve any data access.

 Use it in a for loop to access all elements in the array.

When the incoming element is compared in the compare function and if it is greater than 0, the element content must be exchanged. Another problem arises. The incoming data types are different, and the size of the exchanged content is different. For the sake of compatibility, we can continue to use the above idea to create a swap exchange function, and pass in the addresses of the two functions used by the compare function just now and The size of the memory occupied by an element of the array is sufficient.

The swap function will exchange the data in the two data byte by byte, which can also ensure that all types of arrays can be performed. 

All the preparatory work has been completed, let us complete the simulation of the qsort function.

#include<stdio.h>
#include<string.h>
void print(int arr[10], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

void swap(char*p1,char*p2,int size)
{
	int i = 0;
	char tmp = 0;
	for (i = 0; i < size; i++)
	{
		tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;
	}
}


int compar(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}

void bubble_sort(void* base, int num, int size, int (*cmp)(const void*, const void*))
{
	int i = 0; 
	for (i = 0; i < num-1; i++)
	{
		for (int j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

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]), compar);
	print(arr, sz);
}



int main(void)
{
	test1();
}

If you want to add other types of arrays for sorting, you only need to add a new compare function and the content to be compared. If you want to sort in descending order, just change >0 in the if function to <0. Here is the result of a successful run: 

The main body of the simulated qsort function is the bubble_sort function and the swap function:

void bubble_sort(void* base, int num, int size, int (*cmp)(const void*, const void*))
{
	int i = 0; 
	for (i = 0; i < num-1; i++)
	{
		for (int j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

void swap(char*p1,char*p2,int size)
{
	int i = 0;
	char tmp = 0;
	for (i = 0; i < size; i++)
	{
		tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;
	}
}

 

Guess you like

Origin blog.csdn.net/m0_74755811/article/details/131626987