c使用qsort函数
There are many sorting algorithms, often with several variations, but the C library’s built-in qsort
(Quicksort) function is the best option for sorting arrays in most situations.
排序算法很多,通常会有几种变化,但是C库的内置qsort
(Quicksort)函数是在大多数情况下对数组进行排序的最佳选择。
However, there is more to using qsort
than just throwing an array at a function: you need to provide your own comparator function, the implementation of which can be slightly fiddly if you are sorting anything other than primitive data types. In this article I’ll start off with a simple int-sorting example, and then go on to sort an array of structs, firstly by an int and then by a string.
但是,使用qsort
不仅仅是向函数抛出数组:您还需要提供自己的比较器函数,如果您对除原始数据类型以外的任何其他东西进行排序,则其实现可能会有些麻烦。 在本文中,我将从一个简单的int排序示例开始,然后继续对一个结构数组进行排序,首先是一个int,然后是一个字符串。
If you are interested in how Quicksort works take a look at the Wikipedia article Quicksort here, but for the purposes of this project we’ll just treat the qsort
function as a “black box”.
如果您对Quicksort的工作方式感兴趣,请在此处查看Wikipedia文章Quicksort ,但是出于本项目的目的,我们仅将qsort
函数视为“黑匣子”。
编码 (Coding)
This is a short and simple project so I’ll just keep all the code in one source file. Create a file called qsort.c somewhere convenient, and then type or paste the following. You can clone/download the Github repository if you prefer.
这是一个简短的项目,因此我将所有代码保存在一个源文件中。 在方便的位置创建一个名为qsort.c的文件,然后键入或粘贴以下内容。 如果愿意,可以克隆/下载Github存储库 。
This is the first part of the source code.
这是源代码的第一部分。
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
// --------------------------------------------------------
// STRUCT author
// --------------------------------------------------------
typedef struct author
{
int id;
char name[64];
} author;
//--------------------------------------------------------
// FUNCTION PROTOTYPES
//--------------------------------------------------------
int compare_ints(const void* a, const void* b);
int compare_author_id(const void* a, const void* b);
int compare_author_name(const void* a, const void* b);
void output_ints(int* data, int size);
void output_authors(author* authors, int size);
void sort_integers();
void sort_authors();
//--------------------------------------------------------
// FUNCTION main
//--------------------------------------------------------
void main(void)
{
puts("-----------------");
puts("| codedrome.com |");
puts("| qsort |");
puts("-----------------\n");
sort_integers();
//sort_authors();
}
One of the #includes
is stdlib.h
, needed for the qsort
function. We’ll use the author struct
in an array which we’ll sort by both id
and name
. The first three functions prototyped here are the comparator functions needed by the qsort
function. The next two functions simply output arrays of ints
and authors. The last two functions are called by main
, and will create, sort and output arrays of ints
and authors
respectively.
#includes
是qsort
函数所需的stdlib.h
。 我们将在数组中使用author struct
,并按id
和name
排序。 此处原型化的前三个函数是qsort
函数所需的比较器函数。 接下来的两个函数只是输出ints
和作者数组。 最后两个函数由main
调用,并将分别创建,排序和输出ints
和authors
数组。
排序整数 (Sorting Integers)
As you can see sort_authors
is commented out in main
, as to start with we will just implement sort_integers
and its associated functions. Enter/paste the code for the three functions needed to sort integers into qsort.c.
如您所见,在main
sort_authors
注释掉,首先,我们将实现sort_integers
及其相关功能。 输入/粘贴将整数排序到qsort.c所需的三个函数的代码。
//--------------------------------------------------------
// FUNCTION sort_integers
//--------------------------------------------------------
void sort_integers()
{
int data[16];
srand(time(NULL));
for(int i = 0; i < 16; i++)
{
data[i] = (rand() % 128);
}
puts("Unsorted");
output_ints(data, 16);
qsort(data, 16, sizeof(int), compare_ints);
puts("Sorted");
output_ints(data, 16);
}
//--------------------------------------------------------
// FUNCTION output_ints
//--------------------------------------------------------
void output_ints(int* data, int size)
{
for(int i = 0; i < size; i++)
{
printf("%d\n", data[i]);
}
}
//--------------------------------------------------------
// FUNCTION compare_ints
//--------------------------------------------------------
int compare_ints(const void* a, const void* b)
{
if(*(int*)a < *(int*)b)
return -1;
else if(*(int*)a > *(int*)b)
return 1;
else
return 0;
}
The sort_integers
function creates an array of ints, populates it with random data, and outputs the data. (There is of course the possibility that the random numbers will already be in order. Sometimes writing software can be scary!)
sort_integers
函数创建一个int数组,用随机数据填充它,然后输出数据。 (当然,随机数可能已经整理好了。有时候编写软件可能会很吓人!)
Then we get down to business by calling qsort
. This function takes four arguments:
然后,我们通过致电qsort
开始业务。 该函数有四个参数:
- The array to be sorted 要排序的数组
- The number of items in the array 数组中的项目数
- The size of each item 每个项目的大小
- A pointer to a comparator function taking two void pointer arguments, and returning an int 指向比较器函数的指针,该函数接受两个void指针参数,并返回一个int
After calling qsort
we simply output the data again in its sorted form.
调用qsort
我们只需再次以排序形式输出数据即可。
I won’t insult your intelligence by explaining how output_ints
works, so let’s move straight on to disentangling compare_ints
. As I mentioned above it takes two void
pointers, one to each of the values which need comparing. This makes qsort
general-purpose in that it can sort anything by delegating the actual comparisons to custom functions. It does mean, however, that we have to do something with the pointers to get to the actual values we want to compare, exactly what we need to do with those pointers depends of course on what they are pointing to. The function then returns < 0 if the first argument is “less than” the second (using whatever logic is appropriate for your purposes), > 0 if the first is “more than” the second, and 0 if they are the same. The convention is to return -1, 1 or 0. (If for some reason you need to sort your data in descending order, just swap the return values.)
我不会通过解释output_ints
工作方式来侮辱您的智慧,因此让我们继续output_ints
compare_ints
。 如上所述,它需要两个void
指针,每个需要比较的值都指向一个。 这使qsort
具有通用性,因为它可以通过将实际比较委托给自定义函数来对任何东西进行排序。 但是,这的确意味着我们必须对指针进行一些操作才能获得要比较的实际值,确切地说,我们对这些指针需要执行的操作当然取决于它们所指向的对象。 然后,如果第一个参数“小于”第二个参数(使用适合您的目的的任何逻辑),则函数返回<0;如果第一个参数“大于”第二个参数,则返回0;如果它们相同,则返回0。 约定是返回-1、1或0。(如果由于某种原因需要按降序对数据进行排序,只需交换返回值即可。)
The core bits of the entire function are in the form
整个功能的核心位采用以下形式
*(int*)a
*(int*)a
which when you look at separately don’t look so bad. In this particular case the pointer points to an int
, so firstly we cast the void*
to int*
, and then dereference that pointer to give the actual value. These are then compared, and the appropriate value returned.
当您单独查看时,它看起来并不差。 在这种特殊情况下,指针指向一个int
,因此我们首先将void*
强制转换为int*
,然后取消对该指针的引用以给出实际值。 然后将它们进行比较,并返回适当的值。
Compile the program with the following command in Terminal:
在终端中使用以下命令编译程序:
gcc qsort.c -std=c11 -o qsort
gcc qsort.c -std=c11 -o qsort
and then run it with
然后用
./qsort
./qsort
You should see something like this.
您应该会看到类似这样的内容。
That’s about as simple as qsort
gets, but now we will go on to sort something slightly more complicated, an array of structs. The principle is exactly the same, the big difference is that extracting the values we want to actually compare from the pointers is more involved.
这与qsort
一样简单,但是现在我们将继续对一些稍微复杂的东西进行排序,即结构数组。 原理是完全一样的,最大的不同是从指针中提取我们要实际比较的值的过程更多。
排序结构 (Sorting Structs)
Enter or paste the following into qsort.c, comment out sort_integers
in main
, and uncomment sort_authors
.
输入或粘贴以下为qsort.c,出评论sort_integers
在main
取消注释,并sort_authors
。
//--------------------------------------------------------
// FUNCTION sort_authors
//--------------------------------------------------------
void sort_authors()
{
author authors[6];
authors[0] = (author){.id = 7, .name = "Dickens, Charles"};
authors[1] = (author){.id = 3, .name = "Stevenson, Robert Louis"};
authors[2] = (author){.id = 9, .name = "Bronte, Emily"};
authors[3] = (author){.id = 4, .name = "Gaskell, Elizabeth"};
authors[4] = (author){.id = 1, .name = "Collins, Wilkie"};
authors[5] = (author){.id = 8, .name = "Shelley, Mary"};
puts("Unsorted");
output_authors(authors, 6);
qsort(authors, 6, sizeof(author), compare_author_id);
puts("Sorted by id");
output_authors(authors, 6);
qsort(authors, 6, sizeof(author), compare_author_name);
puts("Sorted by name");
output_authors(authors, 6);
}
//--------------------------------------------------------
// FUNCTION output_authors
//--------------------------------------------------------
void output_authors(author* authors, int size)
{
for(int i = 0; i < size; i++)
{
printf("id: %d name: %s\n", authors[i].id, authors[i].name);
}
}
//--------------------------------------------------------
// FUNCTION compare_author_id
//--------------------------------------------------------
int compare_author_id(const void* a, const void* b)
{
if(((author*)a)->id < ((author*)b)->id)
return -1;
else if(((author*)a)->id > ((author*)b)->id)
return 1;
else
return 0;
}
//--------------------------------------------------------
// FUNCTION compare_author_name
//--------------------------------------------------------
int compare_author_name(const void* a, const void* b)
{
return strcmp(((author*)a)->name, ((author*)b)->name);
}
I’ll gloss over the sort_authors
and output_authors
functions as they are essentially the same as the integer equivalents, except that we create, sort and output an array of authors rather than integers. Note though that we sort the array twice, firstly by id
and secondly by name
. The important functions here are compare_author_id
and compare_author_name
.
我将介绍sort_authors
和output_authors
函数,因为它们与等效的整数基本相同,除了我们创建,排序和输出一个authors数组而不是整数。 请注意,尽管我们对数组进行了两次排序,首先按id
排序,其次按name
排序。 这里的重要功能是compare_author_id
和compare_author_name
。
Let’s look at compare_author_id
first, and specifically getting at the ids from the pointers. The bits of code that do this are in the form:
首先让我们看一下compare_author_id
,特别是从指针中获取ID。 执行此操作的代码位的形式为:
((author*)a)->id
((author*)a)->id
Note that we need to cast the void
pointer to an author
pointer, the whole wrapped in brackets. If we did:
请注意,我们需要将void
指针转换为author
指针,整个指针都放在方括号中。 如果我们这样做:
(author*)a->id
(author*)a->id
we would be attempting to obtain the id
property of a
and then casting that to an author
pointer, which of course is completely wrong and indeed meaningless. If we tried it the compiler would humiliate us with a suitable error message.
我们将试图获得id
的财产a
,然后流延到一个author
的指针,这当然是完全错误的,事实上毫无意义。 如果我们尝试这样做,编译器将以适当的错误消息来羞辱我们。
Now let’s move on to compare_author_name
which works slightly differently as we are now comparing strings. The C library has a built-in string comparison function strcmp
(in string.h) which we will use, but we still need to get at the name properties to pass to strcmp
. This is done in the same way as with id
:
现在让我们转移到compare_author_name
稍微不同的工作,因为我们现在比较字符串。 C库有一个内置的字符串比较函数strcmp
(在string.h中 ),我们将使用它,但是我们仍然需要获取name属性以传递给strcmp
。 这与使用id
方式相同:
((author*)a)->name
((author*)a)->name
The strcmp
function returns the same < 0, 0, > 0 values that qsort
expects so we can just return its return value as-is.
strcmp
函数返回qsort
期望的相同的<0、0,> 0值,因此我们可以按原样返回其返回值。
Build and run the program again and you should get:
再次生成并运行程序,您将获得:
Of course you can create an array of anything, building up data structures of any complexity. It is therefore impossible to give examples of all possible comparator functions to be passed to qsort
, but I hope this article is a good start.
当然,您可以创建任何东西的数组,建立任何复杂性的数据结构。 因此,不可能给出所有可能的比较器函数传递给qsort
示例,但我希望本文是一个好的开始。
翻译自: https://medium.com/programming-in-c/using-the-c-librarys-qsort-function-94380598ff2b
c使用qsort函数