每当您在 C 中遇到排序问题时,您应该立即想到qsort。它是一种高效灵活的排序功能,可以处理任何需要排序的对象。通常让新的 C 程序员害怕的是,您必须编写一个 compare() 函数来告诉 qsort 如何比较和排序数组中的两个 指向元素的指针。比较函数的原型是:
int compare (const void *a, const void *b)
所有a 和b 指针都是指向当前正在比较的数组中的两个元素的指针。您唯一的工作是将它们转换为正确的类型,然后告诉qsort 您希望它们如何比较。先看你的第一个函数,先按age升序比较,然后如果年龄相等,再比较名字,让所有年龄相同的患者按字母顺序排序,从原型开始:
int compasc (const void *a, const void *b) /* qsort compare ascending by age */
{
不知道你的数组元素是什么类型(这里是stuct person),所以a 和b 的类型是pointer-to struct person。您的工作只是将指针a 和b 转换为类型person*,例如
const person *pa = a, *pb = b;
因此,您可以在函数中使用person* 指针pa 和pb,而不是使用void * 指针a 和b。如果年龄不同,比较年龄并返回,例如
if (pa->age != pb->age)
return (pa->age > pb->age) - (pa->age < pb->age);
否则,年龄相等,因此您可以通过name 进行比较,例如
return (strcmp (pa->name, pb->name));
}
这就是您需要的比较功能。完整的应该是:
int compasc (const void *a, const void *b) /* qsort compare ascending by age */
{
const person *pa = a, *pb = b;
if (pa->age != pb->age)
return (pa->age > pb->age) - (pa->age < pb->age);
return (strcmp (pa->name, pb->name));
}
按年龄降序比较操作与age 的比较不同,这将导致按年龄降序排序。无论如何,如果年龄相同,则按姓名按字母顺序排序。
您很好地定义了要在数组中使用的struct person 的最大数量的常量,但您也可以为name 的大小声明一个常量,例如
#define MAXNM 100
#define MAXP 1000
typedef struct person {
char name[MAXNM];
int age;
} person;
(注意: '_' 已从 person_ 中删除,因为 POSIX 对保留以下划线开头/结尾的名称很挑剔——并且不需要您的结构 tag 和 typdef 名字不同)
在main() 中,您最好使用fgets() 或POSIX getline() 等面向行的输入函数读取用户输入的一行。这确保了stdin 中没有多余的未读字符可能会在您下次尝试阅读时咬住您。所以只需声明一个简单的字符数组buf 用作缓冲区来保存所有输入行。然后,您可以使用sscanf() 从线路中获取您需要的信息来处理任何转换。要读取和存储数组中的所有数据,您可以执行以下操作:
int main (void) {
int n_ind = 0;
person individuals[MAXP] = {{ .name = "" }};
while (n_ind < MAXP) {
char buf[MAXP] = ""; /* buffer to hold line of input */
person tmp = { .name = "" }; /* temporary struct to fill */
fputs ("\nenter name & surname: ", stdout);
if (!fgets (buf, MAXNM, stdin))
return 1;
if (*buf == '\n')
break;
buf[strcspn (buf, "\n")] = 0; /* trim trailing '\n' */
strcpy (tmp.name, buf);
fputs ("enter age: ", stdout);
if (!fgets (buf, MAXP, stdin))
return 1;
if (sscanf (buf, "%d", &tmp.age) == 1) /* validate age conversion */
individuals[n_ind++] = tmp; /* add tmp to array update n_ind */
}
(注意: fgets() 还让您能够检查第一个字符是否是 '\n' - 允许您在name 提示用户已完成输入 - 您不需要让他们输入他们将输入的数量。您只需继续添加名称,直到用户单独按 Enter name 提示。)
现在使用qsort,排序变得微不足道,只需传递您的数组、元素数量、每个元素的大小以及您希望qsort 使用的比较函数,剩下的就交给它了,例如
qsort (individuals, n_ind, sizeof *individuals, compasc); /* sort ascending */
这就是按年龄升序排序 - 您的 individuals 数组现在按年龄排序,然后按名称排序。
将它与升序和降序一起放在一起:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXNM 100
#define MAXP 1000
typedef struct person {
char name[MAXNM];
int age;
} person;
int compasc (const void *a, const void *b) /* qsort compare ascending by age */
{
const person *pa = a, *pb = b;
if (pa->age != pb->age)
return (pa->age > pb->age) - (pa->age < pb->age);
return (strcmp (pa->name, pb->name));
}
int compdesc (const void *a, const void *b) /* qsort compare descending by age */
{
const person *pa = a, *pb = b;
if (pa->age != pb->age)
return (pa->age < pb->age) - (pa->age > pb->age);
return (strcmp (pa->name, pb->name));
}
int main (void) {
int n_ind = 0;
person individuals[MAXP] = {{ .name = "" }};
while (n_ind < MAXP) {
char buf[MAXP] = ""; /* buffer to hold line of input */
person tmp = { .name = "" }; /* temporary struct to fill */
fputs ("\nenter name & surname: ", stdout);
if (!fgets (buf, MAXNM, stdin))
return 1;
if (*buf == '\n')
break;
buf[strcspn (buf, "\n")] = 0; /* trim trailing '\n' */
strcpy (tmp.name, buf);
fputs ("enter age: ", stdout);
if (!fgets (buf, MAXP, stdin))
return 1;
if (sscanf (buf, "%d", &tmp.age) == 1) /* validate age conversion */
individuals[n_ind++] = tmp; /* add tmp to array update n_ind */
}
qsort (individuals, n_ind, sizeof *individuals, compasc); /* sort ascending */
puts ("\nascending order by age:");
for (int i = 0; i < n_ind; i++)
printf (" %-24s %2d\n", individuals[i].name, individuals[i].age);
qsort (individuals, n_ind, sizeof *individuals, compdesc); /* sort descending */
puts ("\ndescending order by age:");
for (int i = 0; i < n_ind; i++)
printf (" %-24s %2d\n", individuals[i].name, individuals[i].age);
}
使用/输出示例
按年龄排序,如果年龄相等,则按姓名:
$ ./bin/individualsbyage
enter name & surname: Jeffrey L Davis
enter age: 80
enter name & surname: Yaseen Nur al Din Khoury
enter age: 80
enter name & surname: Joeri Ong
enter age: 80
enter name & surname:
ascending order by age:
Jeffrey L Davis 80
Joeri Ong 80
Yaseen Nur al Din Khoury 80
descending order by age:
Jeffrey L Davis 80
Joeri Ong 80
Yaseen Nur al Din Khoury 80
现在一个简单的例子表明,如果年龄不同,则会获得正确的排序:
$ /bin/individualsbyage
enter name & surname: Jeffrey L Davis
enter age: 81
enter name & surname: Yaseen Nur al Din Khoury
enter age: 80
enter name & surname: Joeri Ong
enter age: 79
enter name & surname:
ascending order by age:
Joeri Ong 79
Yaseen Nur al Din Khoury 80
Jeffrey L Davis 81
descending order by age:
Jeffrey L Davis 81
Yaseen Nur al Din Khoury 80
Joeri Ong 79
qsort 的另一个好处是除了针对快速排序进行了优化之外,它还被 100,000 人使用和测试(如果不是 1,000,000 人)并经过验证。 (不是那种你碰巧在一夜之间拼凑起来的那种)
如果您有其他问题,请查看并告诉我。