简体   繁体   English

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

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

I wrote a code that stores details of persons in a struct which has a dynamically allocated array of pointers (pointing to structures). 我编写了一个代码,该代码在具有动态分配的指针数组(指向结构)的结构中存储人员的详细信息。 These structs are sorted via qsort and should be printed afterwards, but it displays "segmentation fault" right before printing. 这些结构通过qsort排序,应在以后打印,但在打印之前显示“ segmentation fault”。 I assume the problem lies within the printf function and I would be very grateful if someone could explain to me what I have done wrong. 我认为问题出在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;
}

You have a large number of problems, all stemming from your incorrect handling of your array of pointers : 您有很多问题,所有问题都源于对指针数组的不正确处理:

    struct Person *P[10];

When you declare P above, you have an array of 10 pointers, NOT 10 struct Person . 在上面声明P ,您将拥有10个指针的数组,而不是10个struct Person Your allocation, P[i] = (struct Person *) malloc(sizeof(struct Person)); 您的分配P[i] = (struct Person *) malloc(sizeof(struct Person)); is fine, but See: Do I cast the result of malloc? 很好,但是请参见: 我是否强制转换malloc的结果?

You then incorrectly use COUNT_OF(x) to set your loop limits (after hardcoding the 1st to 3 ) which leaves you attempting to qsort and free elements 3-9 that are neither allocated or initialized leading to Undefined Behavior . 然后,您错误地使用COUNT_OF(x)设置了循环限制(将1st硬编码为3 ),从而使您尝试qsortfree既未分配也未初始化的元素3-9 ,从而导致Undefined Behavior (your SegFault can occur anywhere) (您的SegFault可以在任何地方发生)

Your qsort compare functions are wrong. 您的qsort比较功能是错误的。 They are off by one-level-of-indirection. 它们通过一级间接关系关闭。 You are sorting P which is an array of pointers . 您正在排序P ,它是一个指针数组 Since qsort passes a pointer-to-element to your compare function (remember your elements are pointers not struct), qsort is passing a pointer-to-pointer-to-struct as parameters to your compare function. 由于qsort指针指向元素传递给您的compare函数(请记住,您的元素是指针而不是struct),因此qsort会将指针指向指针的结构作为参数传递给了compare函数。 Therefore your compare functions need to provide an additional level of indirection in the type of variable the const void * parameters are converted to. 因此,您的compare函数需要在将const void *参数转换为的变量类型中提供额外的间接级别。 For example: 例如:

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);
}

( note: since the parameters are void* there is no need to cast the assignments within each compare function. Also note: a subtraction of conditionals is used to avoid potential for overflow, eg (a > b) - (a < b) rather than just a - b for age ) 注意:由于参数为void* ,因此无需在每个比较函数中强制转换赋值。另请注意:使用条件值减法可避免潜在的溢出,例如(a > b) - (a < b)而不是不只是age a - b

In the remainder of your code, you need a counter to keep track of the number of elements filled, and use that counter as to pass to qsort and to print the sorted arrays (and free the pointers when done). 在代码的其余部分,您需要一个计数器来跟踪填充的元素数,并使用该计数器将其传递给qsort并打印排序后的数组(并在完成后free指针)。 A simple n counter is used below, eg 下面使用一个简单的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);

Now your code will work. 现在您的代码可以使用了。

Putting it altogether, you can do: 总而言之,您可以执行以下操作:

#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;
}

Example Use/Output 使用/输出示例

$ ./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***

( note: your input routine is horribly fragile and you fail to check the return of every scanf , further inviting Undefined Behavior with the slip of a single keystroke. Always validate Every user-input and Every allocation) 注意:您的输入例程非常脆弱,并且您无法检查每个scanf返回 ,仅通过一次击键就进一步引发了Undefined Behavior 。请始终验证每个用户输入和每个分配)

Memory Use/Error Check 内存使用/错误检查

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed. 在您编写的任何可以动态分配内存的代码中,对于任何分配的内存块,您都有2个责任 :(1) 始终保留指向该内存块起始地址的指针,因此,(2)在没有内存块时可以将其释放需要更长的时间。

It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated. 必须使用一个内存错误检查程序来确保您不尝试访问内存或不要在已分配的块的边界之外/之外进行写入,不要尝试以未初始化的值读取或基于条件跳转,最后确定您可以释放已分配的所有内存。

For Linux valgrind is the normal choice. 对于Linux, valgrind是通常的选择。 There are similar memory checkers for every platform. 每个平台都有类似的内存检查器。 They are all simple to use, just run your program through it. 它们都很容易使用,只需通过它运行程序即可。

$ 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)

Always confirm that you have freed all memory you have allocated and that there are no memory errors. 始终确认已释放已分配的所有内存,并且没有内存错误。

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

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