ホームページ:
まだまだ未知の部分が探索を待っている_データ構造、小規模プロジェクト、C言語の難しさ - CSDNブログ
トピック列:
目次
I.はじめに
1. 最初のパラメータは void*base です。これは、ソートされる配列の最初のオブジェクトへのポインタで、void* に変換されます。
2. 2 番目のパラメータは size_t num です。配列内のベースによってポイントされる要素の数です。size_t は符号なし整数型です。
3. 3 番目のパラメータは size_t size: 配列内の各要素のサイズ (バイト単位) size_t は符号なし整数型です。
4. 4 番目のパラメーターは int (*compar)(const void*p1, const void*p2)) です。2 つの要素を比較する関数へのポインターです。
バブルソートや選択ソートを書くときに、C言語ライブラリ関数のqsort関数のパラメータがなぜあんなに面倒なのか不思議に思っていませんか? なぜ size_t サイズを渡す必要があるのでしょうか? みなさんもそんな疑問はありませんか?今日はそれを詳しく解説していきます!(ライブラリ関数内のqsort関数はクイックソートですが、バブルソートを使用して実装しています)
2.実装の説明
1. 整数配列をソートする
まず、カスタム関数のパラメータを設計する必要がありますが、qsort関数をライブラリ関数内に実装しているため、ライブラリ関数にパラメータを直接記述するだけで済みます。
ここでソートしたいのは整数配列なので、整数配列も必要です。
sz は配列の長さを計算するために使用されます。
質問: カスタム関数の受信配列のパラメーターの型が int* ではなく void* なのはなぜですか?
回答: void* は型なしポインターであり、強制的な型変換を必要とせずにあらゆる型のデータを指すことができます。これは、配列 arr を void*base に渡すと、base の型が int* でない理由に関係なく、void* 型をユニバーサル型として理解できることを意味します。(注: void* 型の変数には逆参照によって直接アクセスしたり、ポインター操作を実行したりすることはできません。上記の操作を実行するには、強制的な型変換が必要です。)
#include<stdio.h>
int cmp(void* x, void* y)
{
return *(int*)x - *(int*)y;
}
//声明自定义排序函数
void qsort_m(void* base, size_t num, size_t size,int (*compar)(const void* p1, const void* p2));
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1 };//提供一个整型数组
int sz = sizeof(arr) / sizeof(arr[0]);//计算数组的长度
qsort_m(arr, sz, sizeof(arr[0]), cmp);//自定义排序函数的调用
int i = 0;
for (i = 0; i < sz; i++)//输出
printf("%d ", arr[i]);
return 0;
}
ソートの実装
void qsort_m(void* base, size_t num, size_t size, int (*compar)(const void* p1, const void* p2)) { int i = 0, j = 0; for (int i = 0; i < num - 1; i++) { for (int j = 0; j < num - 1 - i; j++) { if (compar(((char*)base + j * size), (char*)base + (j + 1) * size) > 0) { //交换 swap((char*)base + j * size, (char*)base + (j + 1) * size,size); } } } }
compar(((char*)base + j * size), (char*)base + (j + 1) * size)、このセクションでは、base を強制的に char* 型に変換する必要があります。その目的は、この char * より多くのデータを表現できる データ型の場合、char* 逆参照は 1 バイトのデータしか取得できませんが、int* などの他の型は逆参照後に char* よりも大きいバイトのデータを取得できるため、char* 型を使用すると、すべてのデータが渡されるので、データ型のサイズを渡す必要がある理由がわかりました。j*size は、配列が次の空間のデータにアクセスできるようにするためです。
最終的には、比較的単純な交換関数が残ります。
void swap(char* buf1, char* buf2, size_t size)
{
int i = 0;
for (int i = 0; i < size; i++)
{
int tmp;
tmp = *(buf1 + i);
*(buf1 + i) = *(buf2 + i);
*(buf2 + i) = tmp;
}
}
合計コード
#include<stdio.h>
int cmp(void* x, void* y)
{
return *(int*)x - *(int*)y;
}
void swap(char* buf1, char* buf2, size_t size);
void qsort_m(void* base, size_t num, size_t size, int (*compar)(const void* p1, const void* p2));
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1 };//提供一个整型数组
int sz = sizeof(arr) / sizeof(arr[0]);//计算数组的长度
qsort_m(arr, sz, sizeof(arr[0]), cmp);//自定义排序函数的调用
int i = 0;
for (i = 0; i < sz; i++)//输出
printf("%d ", arr[i]);
return 0;
}
void qsort_m(void* base, size_t num, size_t size, int (*compar)(const void* p1, const void* p2))
{
int i = 0, j = 0;
for (int i = 0; i < num - 1; i++)
{
for (int j = 0; j < num - 1 - i; j++)
{
if (compar(((char*)base + j * size), (char*)base + (j + 1) * size) > 0)
{
//交换
swap((char*)base + j * size, (char*)base + (j + 1) * size,size);
}
}
}
}
void swap(char* buf1, char* buf2, size_t size)
{
int i = 0;
for (int i = 0; i < size; i++)
{
int tmp;
tmp = *(buf1 + i);
*(buf1 + i) = *(buf2 + i);
*(buf2 + i) = tmp;
}
}
2. qsort でパラメータ cmp 関数を実装する方法
cmp は関数ポインタです。この関数ポインタは関数を指します。この関数の戻り値は int 型です。戻り値が <0 の場合、p1 が指す要素は p2 が指す要素よりも前にあり、戻り値が =0 の場合、p1 が指す要素は p2 が指す要素と同等であり、戻り値が >0 の場合、 p1 が指す要素は、p2 が指す要素の後にあります。
詳しく知りたい方はリンクをクリックして前回の記事を読んでください。
C言語のqsort関数の使い方_まだまだ知られていないブログが開拓を待っている - CSDNブログ
1. 浮動小数点型
int cmp(const void* p1, const void* p2)
{
return *(float*)p1 - *(float*)p2;
}
2. 構造の種類
typedef struct Stu
{
char name[20];
int score;
}Stu;
int cmp(const void* p1, const void* p2)
{
return ((Stu*)p1)->score - ((Stu*)p2)->score;
}
int main()
{
Stu s[3] = { { "张三",50 }, { "王五",70 }, { "李四",60 } };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp);
for (int i = 0; i < sz; i++)
printf("%s %d\n", s[i].name,s[i].score);
return 0;
}
最後に、ご支援ありがとうございました!