繁体   English   中英

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

[英]How to fix “Segmentation fault” while printing string of struct?

我编写了一个代码,该代码在具有动态分配的指针数组(指向结构)的结构中存储人员的详细信息。 这些结构通过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;
}

您有很多问题,所有问题都源于对指针数组的不正确处理:

    struct Person *P[10];

在上面声明P ,您将拥有10个指针的数组,而不是10个struct Person 您的分配P[i] = (struct Person *) malloc(sizeof(struct Person)); 很好,但是请参见: 我是否强制转换malloc的结果?

然后,您错误地使用COUNT_OF(x)设置了循环限制(将1st硬编码为3 ),从而使您尝试qsortfree既未分配也未初始化的元素3-9 ,从而导致Undefined Behavior (您的SegFault可以在任何地方发生)

您的qsort比较功能是错误的。 它们通过一级间接关系关闭。 您正在排序P ,它是一个指针数组 由于qsort指针指向元素传递给您的compare函数(请记住,您的元素是指针而不是struct),因此qsort会将指针指向指针的结构作为参数传递给了compare函数。 因此,您的compare函数需要在将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)而不是不只是age a - b

在代码的其余部分,您需要一个计数器来跟踪填充的元素数,并使用该计数器将其传递给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返回 ,仅通过一次击键就进一步引发了Undefined Behavior 。请始终验证每个用户输入和每个分配)

内存使用/错误检查

在您编写的任何可以动态分配内存的代码中,对于任何分配的内存块,您都有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)

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM