C言語によるqsort関数のシミュレーション実装

qsort 関数に関する予備知識

折り返し電話

コールバック関数は、関数ポインターを通じて呼び出される関数です。
もし、あんたが関数のポインタ(アドレス)をパラメータとして受け取ります別の関数に渡され、このポインターが指す関数を呼び出すために使用される場合、呼び出される関数はコールバック関数です。コールバック関数は、関数の実装者によって直接呼び出されるのではなく、特定のイベントまたは条件が発生したときに、そのイベントまたは条件に応答するために別のパーティによって呼び出されます。
このまま話すと少し抽象的になるかもしれませんが、以下の実際の事例の中で詳しくご紹介します。>

関数ポインタ型の解析

関数ポインタという名前を聞くと、きっとこれはポインタだろうと思うでしょう。実際、それ以外の場合、ポインタはアドレスの格納に使用されるため、関数ポインタ変数を使用して関数アドレスを格納する必要があり、将来はそのアドレスを介して関数を呼び出すことができます。
では、どうやって関数のアドレスを取得するのでしょうか? コードを書いてみましょう:
ここに画像の説明を挿入します
デバッグを通じて、関数にアドレスがあることを理解するのは難しくありません。関数名は関数のアドレスです。もちろん、関数のアドレスは関数名を通じて取得することもできます&では、どうやって関数のアドレスを取得するのでしょうか?今回は、関数ポインターが導入されていますので、Add関数を例に挙げてみましょう:
ここに画像の説明を挿入します

qsort 関数の使用法と関連パラメータ

cplusplus.comの説明を見てみましょう!
ここに画像の説明を挿入します
ここに画像の説明を挿入します
(1) 最初のパラメータは、ソートされる最初の要素のアドレスです (ここではbaseアドレスを指します)。要素の型が不明なので、void*変数名として使用
(2) 2 番目のパラメータはソートする要素の数;
(3) 3 番目のパラメータはソートする各要素のサイズ; (4) 3 番目のパラメータは関数
を指す関数ポインタcompar2 つの要素を比較できます。この関数は自分で実装する必要があります。関数の戻り値の型が で、パラメータの型が であること
がわかります認識しなければならないのはcomparintconst void*型のパラメータvoid*のサイズを直接比較することはできないため、比較する前に型変換を実行する必要があります。

バブルソートアルゴリズム

元のバブルソートアルゴリズムを見てみましょう:

void bubble_sort(int arr[], int sz)
{
    
    
	int i = 0;
	for (i = 0; i < sz; i++)
	{
    
    
		int flag = 1;
		for (int 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;
				flag = 0;
			}
		}
		if (flag == 1)
			break;
	}
}
int main()
{
    
    
	int arr[] = {
    
     1,5,3,6,8,8,5,9,6,5 };
	int len = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, len);
	for (int i = 0; i < len; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
	putchar('\n');
	return 0;
}

図:
ここに画像の説明を挿入します
上の図を通して、何回ループする必要があるかを知っていれば、実際には何も難しいことはありません。2 レベルforのループを使用してバブル ソートを実装できます。外側のlenレベル、内側のlen-iレベルの循環を覚えておいてください。

シミュレーションの実装方法の紹介

バブル ソート アルゴリズムを理解したところで、それを同様のqsort関数に改良するにはどうすればよいでしょうか? 実際には、まだ外側の層と記憶層
の 2 層のループです変更されたのは比較関数ですソートする要素を直接交換するのは難しいため、ここでは交換関数を使用しますこれら 2 つの関数の関数は に変換されました。なぜですか? 特定の要素の型がわからないため、走査を容易にするために、要素をに変換します。このとき、必ず誰かが、 などのタイプに変身しないのです答えは、ソートする要素のサイズにあります。ソートする各要素が構造体であり、それぞれが 9 バイトを占める場合、4 バイトと2 バイトは割り切れず、不完全な交換が発生します。これは、関数が毎回比較対象の要素の最初のバイト アドレスを渡し、合計を計算するたびに 1 つの要素をスキップできるようにするためでもあります。lenlen-icmp((char*)base + j * width, (char*)base + (j + 1) * width)
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
basechar*void*char*int*short*
intshortSwapj*width(j+1)*width

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

したがって、この関数Swapでバイトが交換されるたびにwidth1 つの要素が交換され、これはforループで実現できます。

ソースコード

struct Stu
{
    
    
	char name[20];
	int age;
};
//比较数组元素
int cmp_int(const void* e1, const void* e2)
{
    
    
	return *(int*)e1 - *(int*)e2;
}
//比较结构体中的年龄
int cmp_str_by_age(const void* e1, const void* e2)
{
    
    
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//比较结构体中的字符串
int cmp_str_by_name(const void* e1, const void* e2)
{
    
    
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//交换两个元素(因为不知道元素类型,所以以一字节为单位逐个交换,至width大小)
void Swap(char* buf1, char* buf2, size_t width)
{
    
    
	for (int i = 0; i < width; i++)
	{
    
    
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++; buf2++;
	}
}
//其中调用函数指针,指向cmp函数
void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
    
    
	int i = 0;
	for (i = 0; i < sz; i++)
	{
    
    
		int flag = 1;//测试是否已排序完成,提高效率
		for (int j = 0; j < sz - 1 - i; j++)
		{
    
    
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
    
    
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
				flag = 0;
			}
		}
		if (flag == 1)
			break;
	}
}
//整形数组
void test1()
{
    
    
	int arr1[] = {
    
     1,5,3,6,4,9,2,9,6,5 };
	int len = sizeof(arr1) / sizeof(arr1[0]);
	bubble_sort(arr1, len, sizeof(arr1[0]), cmp_int);
	for (int i = 0; i < len; i++)
	{
    
    
		printf("%d ", arr1[i]);
	}
	putchar('\n');
}
//结构体数组--比较age,整形
void test2()
{
    
    
	struct Stu arr2[] = {
    
     {
    
    "zhangsan",16},{
    
    "lisi",37},{
    
    "wangwu",22} };
	int len = sizeof(arr2) / sizeof(arr2[0]);
	bubble_sort(arr2, len, sizeof(arr2[0]), cmp_str_by_age);
}
//结构体数组--比较name,字符串
void test3()
{
    
    
	struct Stu arr3[] = {
    
     {
    
    "zhangsan",16},{
    
    "lisi",37},{
    
    "wangwu",22} };
	int len = sizeof(arr3) / sizeof(arr3[0]);
	bubble_sort(arr3, len, sizeof(arr3[0]), cmp_str_by_name);
	struct Stu* prv = arr3;
	for (int i = 0; i < len; i++)
	{
    
    
		printf("%s  ", prv->name);
		prv++;
	}
}
int main()
{
    
    
//三个测试函数
	test1();
	//test2();
	test3();
	
	return 0;
}

ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/2301_77404033/article/details/132368136