如何在打印结构字符串时修复“分段错误”?

时间:2019-06-18 21:32:25

标签: c segmentation-fault qsort

我写了一个代码,将一个人的详细信息存储在一个结构中,该结构具有动态分配的指针数组(指向结构)。这些结构通过qsort排序,应在以后打印,但在打印之前显示“ segmentation fault”。我认为问题出在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 个答案:

答案 0 :(得分:2)

您有很多问题,都是由于对指针数组的错误处理引起的:

    struct Person *P[10];

在上面声明P时,将有10个指针的数组,而不是10个struct Person。您的分配P[i] = (struct Person *) malloc(sizeof(struct Person));很好,但请参见:Do I cast the result of malloc?

然后,您错误地使用COUNT_OF(x)来设置循环限制(在将第一个硬编码为3之后),这使您尝试尝试qsortfree元素{{1} }既未分配也未初始化,从而导致未定义行为。 (您的SegFault可以在任何地方发生)

您的3-9比较函数是错误的。它们通过一级间接关系关闭。您正在排序qsort,它是一个指针数组。由于P pointer-to-element 传递给您的比较函数(请记住您的元素是 pointers 而不是struct),因此qsort传递了< em> pointer-to-pointer-to-struct 作为比较函数的参数。因此,您的比较函数需要在将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)的{​​{1}})

在代码的其余部分,您需要一个计数器来跟踪填充的元素数,并使用该计数器传递给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; } 返回,从而进一步引发了未定义行为 em>,只需轻按一下即可。始终 验证 每个用户输入和每个分配)

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时可以释放

当务之急是使用一个内存错误检查程序来确保您不会尝试访问内存或在已分配的块的边界之外/之外进行写入,不要试图以未初始化的值读取或基于条件跳转,最后,以确认您释放了已分配的所有内存。

对于Linux,$ ./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

始终确认已释放已分配的所有内存,并且没有内存错误。