こんにちは、みんな!前回の記事(C言語の高度な部分を徹底解説(上級ポインター2)_要するに超謙虚なブログです - CSDNブログ)でコールバック関数の予備的な説明と簡単な使用例を記載しました。スペースが限られていたので、先に進めませんでしたが、今日はさらに詳しい説明を追加します。
目次
3. バブルソートを使用して qsort() をシミュレートする
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 つのパラメータを受け取ります。
base
: ソートする配列の最初の要素へのポインタ。nmemb
: 配列の要素数を示します。size
: 各要素のサイズをバイト単位で示します。compar
: 2 つの要素を比較するために使用されるコールバック関数へのポインタコールバック関数は、
compar
2 つの要素のサイズ関係を比較するために使用されます。比較する要素へのポインターである 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]); //排序完后对数组进行打印,验证排序是否成功
}
}
もちろん、このシミュレーション方法にはまだ多くの欠点があります。
- バブルソートはシンプルだが非効率的
- バイトごとの位置の交換は、要素のタイプやサイズに関係なく、あらゆるタイプの要素に対して機能します。ただし、効率が悪いというデメリットもあります
今後は新しい知識を学んで成長していけたらと思っています。