Всякий раз, когда вы сталкиваетесь с проблемой сортировки в 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;
Таким образом, вместо void *
указателей a
& b
, у вас есть person*
указатели pa
и pb
для работы в функции. Если возраст отличается, сравните возраст и верните, например,
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;
( note: '_'
был удален из person_
, поскольку POSIX требователен к резервированию имен, начинающихся с / заканчивается на подчеркивания - и вам не нужно, чтобы ваши теги struct и 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 */
}
( note: fgets()
также дает вам возможность проверить, является ли первый символ '\n'
- позволяет вам использовать только Enter в приглашении name
в качестве указания на то, что пользователь завершил ввод - вам не нужно, чтобы они вводили, сколько они будут вводить. Вы просто продолжаете добавлять имена, пока пользователь не нажмет Ввод в одиночку по приглашению 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);
}
Пример использования / Вывод
Сортировка по возрасту, если возраст равен, то по имени:
* 109 9 *
Теперь краткий пример, показывающий, что если возраст различается, получаются правильные сортировки:
$ /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 человек (если не 100 000 человек) и подтверждено. (не совсем так, что вы, случается, бьетесь в одночасье)
Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.