Как исправить ошибку сегментации при печати строки struct? - PullRequest
1 голос
/ 19 июня 2019

Я написал код, который хранит данные о людях в структуре, которая имеет динамически размещенный массив указателей (указывающих на структуры). Эти структуры сортируются через qsort и должны быть напечатаны позже, но перед печатью отображается «ошибка сегментации». Я предполагаю, что проблема заключается в функции printf, и я был бы очень признателен, если бы кто-нибудь мог объяснить мне, что я сделал неправильно.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX 2
#define COUNT_OF(x) (sizeof(x) / sizeof(0[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 *ix = (struct Person *)x;
    struct Person *iy = (struct Person *)y;
    return strcmp(ix->firstname, iy->firstname); 
    }


int Person_cmp_lastname(const void* x, const void* y ) {
    struct Person *ix = (struct Person *)x;
    struct Person *iy = (struct Person *)y;
    return strcmp(ix->lastname, iy->lastname); 
}
int Person_cmp_age(const void* x, const void* y) {
    struct Person *px = (struct Person *) x;
    struct Person *py = (struct Person *) y;
    return px->age - py->age;
}

int main(){;
    int choice;
    struct Person *P[10];
    printf("\t***PROGRAM TO SORT PERSONS***\n\n");
    for(int 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");
    }
    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, COUNT_OF(P), sizeof(struct Person), Person_cmp_firstname);
                printf("\t***DONE***\n\n");
                for(unsigned int i=0; i<3; 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, COUNT_OF(P), sizeof(struct Person ), Person_cmp_lastname);
                printf("\t***DONE***\n\n");
                for(unsigned int i=0; i<3; 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, COUNT_OF(P), sizeof(struct Person), Person_cmp_age);
                printf("\t***DONE***\n\n");
                for(unsigned int i=0; i<3; 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 (int j = 0; j < 3; j++) {
                    free(P[j]);
                }
                exit(0);
            default:
                printf("\t***INVALID OPTION***\n\n");
                break;
        }
    }while(1);
    return EXIT_SUCCESS;
}

1 Ответ

2 голосов
/ 19 июня 2019

У вас большое количество проблем, все из-за неправильной обработки массива указателей :

    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)

Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.

...