У вас большое количество проблем, все из-за неправильной обработки массива указателей :
struct Person *P[10];
Когда вы объявляете P
выше, у вас есть массивиз 10 указателей, НЕ 10 struct Person
.Ваше распределение, P[i] = (struct Person *) malloc(sizeof(struct Person));
в порядке, но смотрите: Я разыгрываю результат malloc?
Затем вы неправильно используете COUNT_OF(x)
для установки пределов вашего цикла (после жесткого кодирования 1-го до3
), из-за которого вы пытаетесь qsort
и free
элементы 3-9
, которые не были ни выделены, ни инициализированы, что приводит к неопределенному поведению .(ваш SegFault может произойти где угодно)
Ваши qsort
функции сравнения неверны.Они отключены одним уровнем косвенности.Вы сортируете P
, который является массивом указателей .Поскольку qsort
передает указатель на элемент в вашу функцию сравнения (помните, что ваши элементы указатели не являются структурами), qsort
передает указатель на-указатель на структуру в качестве параметров вашей функции сравнения.Поэтому функции сравнения должны обеспечивать дополнительный уровень косвенности в типе переменной, в которую преобразуются параметры const void *
.Например:
int Person_cmp_firstname(const void* x, const void* y) {
struct Person * const *ix = x;
struct Person * const * iy = y;
return strcmp((*ix)->firstname, (*iy)->firstname);
}
int Person_cmp_lastname(const void* x, const void* y ) {
struct Person * const *ix = x;
struct Person * const *iy = y;
return strcmp((*ix)->lastname, (*iy)->lastname);
}
int Person_cmp_age(const void* x, const void* y) {
struct Person * const *px = x;
struct Person * const *py = y;
return ((*px)->age > (*py)->age) - ((*px)->age < (*py)->age);
}
( примечание: , поскольку параметры void*
, нет необходимости приводить назначения в каждой функции сравнения. Также примечание: вычитание условных выражений используется, чтобы избежать потенциального переполнения, например, (a > b) - (a < b)
, а не просто a - b
для age
)
В оставшейся части кода необходим счетчик для отслеживания числа.заполненных элементов, и используйте этот счетчик для перехода к qsort
и для печати отсортированных массивов (и free
указателей, когда закончите).Ниже используется простой счетчик n
, например,
int main(){;
int choice, i, n;
struct Person *P[10];
printf("\t***PROGRAM TO SORT PERSONS***\n\n");
for(i=0; i<3; i++){
P[i] = (struct Person *) malloc(sizeof(struct Person));
printf("Firstname: ");
scanf("%s", P[i]->firstname);
printf("Lastname: ");
scanf("%s", P[i]->lastname);
printf("Age: ");
scanf("%d", &P[i]->age);
printf("\t***NEXT PERSON***\n\n");
}
n = i; /* saves the number of filled struct as n */
...
qsort(P, n, sizeof *P, Person_cmp_firstname);
for(i=0; i<n; i++){
printf( "Firstname: %s\t| Lastname: %s\t| Age: %d\n",
P[i]->firstname, P[i]->lastname, P[i]->age );
...
case 4:
printf("\t***EXITING PROGRAM***\n\n");
for (i = 0; i < n; i++) {
free(P[i]);
}
exit(0);
Теперь ваш код будет работать.
Если положить его в целом, вы можете сделать:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX 2
#define COUNT_OF(x) (sizeof(x) / sizeof(*x)) //length of arrays at compile time
struct Person {
char firstname[64];
char lastname[64];
int age;
};
int Person_cmp_firstname(const void* x, const void* y) {
struct Person * const *ix = x;
struct Person * const * iy = y;
return strcmp((*ix)->firstname, (*iy)->firstname);
}
int Person_cmp_lastname(const void* x, const void* y ) {
struct Person * const *ix = x;
struct Person * const *iy = y;
return strcmp((*ix)->lastname, (*iy)->lastname);
}
int Person_cmp_age(const void* x, const void* y) {
struct Person * const *px = x;
struct Person * const *py = y;
return ((*px)->age > (*py)->age) - ((*px)->age < (*py)->age);
}
int main(){;
int choice, i, n;
struct Person *P[10];
printf("\t***PROGRAM TO SORT PERSONS***\n\n");
for(i=0; i<3; i++){
P[i] = (struct Person *) malloc(sizeof(struct Person));
printf("Firstname: ");
scanf("%s", P[i]->firstname);
printf("Lastname: ");
scanf("%s", P[i]->lastname);
printf("Age: ");
scanf("%d", &P[i]->age);
printf("\t***NEXT PERSON***\n\n");
}
n = i;
do{
printf("\n\t***CHOOSE HOW TO SORT***\n\n\tBy firstname: 1\n\tBy lastname: 2\n\tBy age: 3\n\tExit Program: 4\n\n");
scanf("%d", &choice);
switch(choice){
case 1:
printf("\t***SORTING BY FIRSTNAME...***\n\n");
qsort(P, n, sizeof *P, Person_cmp_firstname);
printf("\t***DONE***\n\n");
for(i=0; i<n; i++){
printf( "Firstname: %s\t| Lastname: %s\t| Age: %d\n", P[i]->firstname, P[i]->lastname, P[i]->age );
}
break;
case 2:
printf("\t***SORTING BY LASTNAME...***\n\n");
qsort(P, n, sizeof *P, Person_cmp_lastname);
printf("\t***DONE***\n\n");
for(i=0; i<n; i++){
printf( "Firstname: %s\t| Lastname: %s\t| Age: %d\n", P[i]->firstname, P[i]->lastname, P[i]->age );
}
break;
case 3:
printf("\t***SORTING BY AGE...***\n\n");
qsort(P, n, sizeof *P, Person_cmp_age);
printf("\t***DONE***\n\n");
for(i=0; i<n; i++){
printf( "Firstname: %s\t| Lastname: %s\t| Age: %d\n",P[i]->firstname, P[i]->lastname, P[i]->age );
}
break;
case 4:
printf("\t***EXITING PROGRAM***\n\n");
for (i = 0; i < n; i++) {
free(P[i]);
}
exit(0);
default:
printf("\t***INVALID OPTION***\n\n");
break;
}
}while(1);
return EXIT_SUCCESS;
}
Пример использования / Вывод
$ ./bin/person_struct
***PROGRAM TO SORT PERSONS***
Firstname: Porky
Lastname: Pig
Age: 83
***NEXT PERSON***
Firstname: Daffy
Lastname: Duck
Age: 93
***NEXT PERSON***
Firstname: Mickey
Lastname: Mouse
Age: 100
***NEXT PERSON***
***CHOOSE HOW TO SORT***
By firstname: 1
By lastname: 2
By age: 3
Exit Program: 4
2
***SORTING BY LASTNAME...***
***DONE***
Firstname: Daffy | Lastname: Duck | Age: 93
Firstname: Mickey | Lastname: Mouse | Age: 100
Firstname: Porky | Lastname: Pig | Age: 83
***CHOOSE HOW TO SORT***
By firstname: 1
By lastname: 2
By age: 3
Exit Program: 4
3
***SORTING BY AGE...***
***DONE***
Firstname: Porky | Lastname: Pig | Age: 83
Firstname: Daffy | Lastname: Duck | Age: 93
Firstname: Mickey | Lastname: Mouse | Age: 100
***CHOOSE HOW TO SORT***
By firstname: 1
By lastname: 2
By age: 3
Exit Program: 4
4
***EXITING PROGRAM***
( примечание: ваша процедура ввода ужасно хрупкая, и вы не можете проверить возврат каждого scanf
, далее приглашая Неопределенное поведение с нажатием одной клавиши. Всегда подтверждение Каждый пользовательский ввод и каждое распределение)
Использование памяти / проверка ошибок
В любом написанном вами коде, который динамически распределяет память, у вас есть 2 обязанностей в отношении любого выделенного блока памяти: (1) всегда сохраняйтеуказатель на начальный адрес для блока памяти, поэтому (2) он может быть освобожден , когда он больше не нужен.
Обязательно, чтобы вы использовали памятьпрограмма проверки ошибок, чтобы убедиться, что вы не пытаетесь получить доступ к памяти или писать за пределами / за пределами вашеговыделенный блок, попытка прочитать или основать условный переход на неинициализированном значении и, наконец, подтвердить, что вы освобождаете всю выделенную память.
Для Linux valgrind
- нормальный выбор.Для каждой платформы есть похожие проверки памяти.Все они просты в использовании, просто запустите вашу программу через нее.
$ valgrind ./bin/person_struct
==2078== Memcheck, a memory error detector
==2078== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==2078== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==2078== Command: ./bin/person_struct
==2078==
***PROGRAM TO SORT PERSONS***
Firstname: John
Lastname: Wayne
Age: 91
***NEXT PERSON***
Firstname: Jane
Lastname: Doe
Age: 101
***NEXT PERSON***
Firstname: Mickey
Lastname: Mouse
Age: 99
***NEXT PERSON***
***CHOOSE HOW TO SORT***
By firstname: 1
By lastname: 2
By age: 3
Exit Program: 4
1
***SORTING BY FIRSTNAME...***
***DONE***
Firstname: Jane | Lastname: Doe | Age: 101
Firstname: John | Lastname: Wayne | Age: 91
Firstname: Mickey | Lastname: Mouse | Age: 99
***CHOOSE HOW TO SORT***
By firstname: 1
By lastname: 2
By age: 3
Exit Program: 4
2
***SORTING BY LASTNAME...***
***DONE***
Firstname: Jane | Lastname: Doe | Age: 101
Firstname: Mickey | Lastname: Mouse | Age: 99
Firstname: John | Lastname: Wayne | Age: 91
***CHOOSE HOW TO SORT***
By firstname: 1
By lastname: 2
By age: 3
Exit Program: 4
3
***SORTING BY AGE...***
***DONE***
Firstname: John | Lastname: Wayne | Age: 91
Firstname: Mickey | Lastname: Mouse | Age: 99
Firstname: Jane | Lastname: Doe | Age: 101
***CHOOSE HOW TO SORT***
By firstname: 1
By lastname: 2
By age: 3
Exit Program: 4
4
***EXITING PROGRAM***
==2078==
==2078== HEAP SUMMARY:
==2078== in use at exit: 0 bytes in 0 blocks
==2078== total heap usage: 3 allocs, 3 frees, 396 bytes allocated
==2078==
==2078== All heap blocks were freed -- no leaks are possible
==2078==
==2078== For counts of detected and suppressed errors, rerun with: -v
==2078== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.