C言語の応用部分の詳細解説(定番コールバック関数qsort()の詳細解説とシミュレーション実装)

こんにちは、みんな!前回の記事(C言語の高度な部分を徹底解説(上級ポインター2)_要するに超謙虚なブログです - CSDNブログ)でコールバック関数の予備的な説明と簡単な使用例を記載しました。スペースが限られていたので、先に進めませんでしたが、今日はさらに詳しい説明を追加します。


目次

1. コールバック関数の意味

2.qsort()関数 

1.説明する 

 2.例

3. バブルソートを使用して qsort() をシミュレートする

1.メイン機能

2.bubble_qsort()

3.cmp()

4.スワップ()

 合計コード:


1. コールバック関数の意味

コールバック関数は、関数ポインターを通じて呼び出される関数です。関数ポインタ (アドレス) をパラメータとして別の関数に渡し、このポインタがそれが指す関数を呼び出すために使用される場合、それをコールバック関数と言います。コールバック関数は、関数の実装者によって直接呼び出されるのではなく、特定のイベントまたは条件が発生したときに、そのイベントまたは条件に応答するために別のパーティによって呼び出されます。 


2.qsort()関数 

1.説明する 

cplusplus の Web サイトによると、次のようになります。

 翻訳は次のとおりです。

qsort 関数は C 言語標準ライブラリの関数であり、配列を迅速にソートするために使用されます。その声明全文は次のとおりです。

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

qsort 関数は 4 つのパラメータを受け取ります。

  1. base: ソートする配列の最初の要素へのポインタ。
  2. nmemb: 配列の要素数を示します。
  3. size: 各要素のサイズをバイト単位で示します。
  4. compar: 2 つの要素を比較するために使用されるコールバック関数へのポインタ

コールバック関数は、compar2 つの要素のサイズ関係を比較するために使用されます。比較する要素へのポインターである 2 つのパラメーターを受け入れます。コールバック関数は、2 つの要素間のサイズ関係を示す整数値を返す必要があります。負の数値が返された場合は、最初の要素が 2 番目の要素より小さいことを意味します。正の数値が返された場合は、最初の要素が 2 番目の要素より大きいことを意味します。ゼロを返した場合は、2 番目の要素が大きいことを意味します。要素が等しい。

通常、このコールバック関数は自分で記述する必要があります。

//升序排序  针对整型的排序:
int compare (const void * a, const void * b)
 {
     return ( *(int*)a - *(int*)b );
 }

//降序排列
 int compare (const void * a, const void * b)
 {
     return ( *(int*)b - *(int*)a );
 }

 2.例

整数の配列をソートする

int compare(const void *a, const void *b) {
    return (*(int*)a - *(int*)b);
}

int main() {
    int arr[] = {5, 3, 8, 2, 1, 4};
    int size = sizeof(arr) / sizeof(arr[0]);
    
    qsort(arr, size, sizeof(int), compare);
    
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    
    return 0;
}

 

 同時に、他のデータ型をソートすることもできます。以下は構造のソートです。

 struct Student{
    char name[20];
    int score;
} ;

int compare(const void* a, const void* b) {
    return ((struct Student*)a)->score - ((struct Student*)b)->score;
}

int main() {
   struct Student students[] = {
        {"Alice", 85},
        {"Bob", 92},
        {"Charlie", 78},
        {"David", 80},
        {"Eva", 88}
    };
    int size = sizeof(students) / sizeof(students[0]);

    qsort(students, size, sizeof(struct Student), compare);

    printf("按照成绩排序后的学生列表:\n");
    for (int i = 0; i < size; i++) {
        printf("姓名:%s,成绩:%d\n", students[i].name, students[i].score);
    }

    return 0;
}


3. バブルソートを使用して qsort() をシミュレートする

1.メイン機能

ここで main は最も基本的な処理のみを実行し、その後 bubble_qsort() 関数に入ります。 

int main()
{
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };     //定义整型数组并初始化
	int sz = sizeof(arr) / sizeof(arr[0]);    //计算数组长度
	int i = 0;
	bubble_sort(arr, sz, sizeof(arr[0]), cmp);    //模拟qsort函数实现冒泡排序
	for (i = 0; i < sz; i++)                   
	{
		printf("%d ", arr[i]);                     //排序完后对数组进行打印,验证排序是否成功
	}
}

2.bubble_qsort()

バブル ソート関数bubble_sort。ソートする配列arr、配列の長さsz、各要素のサイズ、widthおよび比較関数の4 つのパラメータを受け入れますcmpバブル ソート関数は、2 レベルのループを使用してバブル ソート プロセスを実装します。外側のループはバブル ソート パスの数を制御し、内側のループは各パスで比較する必要がある要素のペアを走査します。バブル ソートの各パスで、比較関数によって返された結果が 0 より大きい場合、隣接する 2 つの要素が交換され、大きい方の要素が後方に移動されます。

  • 仮パラメータの型は void* arr で、任意の型ポインタを受け入れることができることがわかります。
  • 比較するパラメータを比較関数 cmp()に渡します。

1 つ目は: (char*)arr + (j * width) まず void* を char* に変換し、次に j*width を追加します。width は各要素のサイズで、j*width は追加する必要があるものです。バイト数、つまり (char*)arr + (j * width) が j 番目の要素の最初のバイトのアドレスになります

void bubble_sort(void* arr, int sz, int width, int(*cmp)(void* e1, void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		//冒泡排序趟数
		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);
			}
		}
	}
}

3.cmp()

char* 型のポインタが渡されますが、キャスト後の 4 バイトにアクセスします。 

int cmp(void* e1, void* e2)   //所选择的比较方法
{
	return *((int*)e1) - *((int*)e2);
}

4.スワップ()

関数のパラメータには、交換する必要がある 2 つの要素を指す 2 つのポインタp1と、および各要素のサイズを示すp2整数が含まれます。width

関数内では、一時変数を使用して、t交換中に一時的な値を保持します。次に、ループを使用して各バイトを反復処理し、2 つの要素をバイトごとに交換します。

void swap(char* p1, char* p2, int width)   //实现数组元素的交换
{
	int t = 0;
	int i = 0;
	for (i = 0; i < width; i++)
	{
		t = *p1;
		*p1 = *p2;
		*p2 = t;
		p1++;
		p2++;
	}
}

 元のコード:

#include<stdio.h>
 
//仿qsort函数重写冒泡排序
int cmp(void* e1, void* e2)   //所选择的比较方法
{
	return *((int*)e1) - *((int*)e2);
}
void swap(char* p1, char* p2, int width)   //实现数组元素的交换
{
	int t = 0;
	int i = 0;
	for (i = 0; i < width; i++)
	{
		t = *p1;
		*p1 = *p2;
		*p2 = t;
		p1++;
		p2++;
	}
}
void bubble_sort(void* arr, int sz, int width, int(*cmp)(void* e1, void* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		//冒泡排序趟数
		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 main()
{
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };     //定义整型数组并初始化
	int sz = sizeof(arr) / sizeof(arr[0]);    //计算数组长度
	int i = 0;
	bubble_sort(arr, sz, sizeof(arr[0]), cmp);    //模拟qsort函数实现冒泡排序
	for (i = 0; i < sz; i++)                   
	{
		printf("%d ", arr[i]);                     //排序完后对数组进行打印,验证排序是否成功
	}
}

 もちろん、このシミュレーション方法にはまだ多くの欠点があります。

  1. バブルソートはシンプルだが非効率的
  2. バイトごとの位置の交換は、要素のタイプやサイズに関係なく、あらゆるタイプの要素に対して機能します。ただし、効率が悪いというデメリットもあります

今後は新しい知識を学んで成長していけたらと思っています。 

 

おすすめ

転載: blog.csdn.net/qq_74415153/article/details/133207122