((C)在指针strcpy上获得分段错误

问题描述 投票:0回答:2

我是C语言的新手,整个早晨我一直被这段代码所困扰。它可以毫无问题地进行编译,但是在执行时会失败。如果您有任何想法可以帮助我解决此问题,请给我留言。任何评论将不胜感激。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct phonebook {
    char name[20];
    char phoneNum[20];
} Phonebook;

int bookSize=1;

void load(Phonebook **book);
void insert(Phonebook **book);
void delete(Phonebook **book);
void search(Phonebook *book);
void print(Phonebook *book);
void save(Phonebook *book);

int main(void) {
    Phonebook *book = (Phonebook *)calloc(sizeof(Phonebook), bookSize);
    load(&book);
    int menuInput=0;
    while(menuInput != 5) {
        puts("***** MENU *****");
        puts("1. Insert");
        puts("2. Delete");
        puts("3. Search");
        puts("4. Print All");
        puts("5. Exit");
        printf(">> ");
        scanf("%d", &menuInput);

        switch(menuInput) {
            case 1 : insert(&book); break;
            case 2 : delete(&book); break;
            case 3 : search(book); break;
            case 4 : print(book); break;
            case 5 : break;
            default : puts("enter correct command"); break;
        }
    }
    save(book);
    free(book);
    puts("\nexit\n");
    return 0;
}

void load(Phonebook **book) {
    FILE *fp = fopen("phonebook.txt", "rt");
    if(fp == NULL) {
        FILE *fp = fopen("phonebook.txt", "wt");
        fclose(fp);
        puts("Welcome! It looks like you don't have an existing phonebook.");
        puts("A new phonebook has been created.\n");
        return;
    }
    else {
        char temp[20];
        int i=0;
        while(fscanf(fp, "%s", temp) != EOF) {
            strcpy(book[i]->name, temp);
            fscanf(fp, "%s", temp);
            strcpy(book[i]->phoneNum, temp);
            i++;
            bookSize++;
            *book = (Phonebook *)realloc(*book, sizeof(Phonebook) * (bookSize));
        }
        fclose(fp);
        printf("Loaded %d contacts\n", bookSize-1);
    }
}

void insert(Phonebook **book) {
    puts("\nCreate a new contact");
    getchar();
    char temp[20];
    printf("Name : ");
    fgets(temp, 20, stdin);
    //temp[strlen(temp)-1]=0;
    strcpy(book[bookSize-1]->name, temp);
    //fgets(book[bookSize-2]->name, 20, stdin);
    //book[bookSize-2]->name[strlen(book[bookSize-2]->name)-1]=0;
    printf("Phone : ");
    fgets(temp, 20, stdin);
    //temp[strlen(temp)-1]=0;
    strcpy(book[bookSize-1]->phoneNum, temp);
    //fgets(book[bookSize-2]->phoneNum, 20, stdin);
    //book[bookSize-2]->phoneNum[strlen(book[bookSize-2]->phoneNum)-1]=0;
    puts("Done!\n");
    bookSize++;
    *book = (Phonebook *)realloc(*book, sizeof(Phonebook) * bookSize);
}

void delete(Phonebook **book) {}

void search(Phonebook *book) {}

void print(Phonebook *book) {
    if(bookSize == 1) {
        puts("\nempty\n");
        return;
    }
    puts("");
    for(int i=0; i<bookSize-1; i++) {
        printf("Name : %-10s  Phone : %s\n", book[i].name, book[i].phoneNum);
    }
    puts("");
}

void save(Phonebook *book) {
    FILE *fp = fopen("phonebook.txt", "wt");
    for(int i=0; i<bookSize-1; i++) {
        fprintf(fp, "%s\n%s\n", book[i].name, book[i].phoneNum);
    }
    fclose(fp);
    printf("\nSaved %d contacts", bookSize-1);
}
Segmentation fault (core dumped)

**很抱歉删除了我认为与“代码无关”的部分代码!我已经将整个代码添加到了帖子中。谢谢!

c pointers struct strcpy
2个回答
2
投票

正如您的其他答案所表明的,您正在遍历双重间接的详细信息。

您正在将电话簿维护为一系列结构。在main中,变量book是指向该数组中第一个结构的指针。第二个将紧随其后在内存中,第三个将紧随其后,etc。这一切都很好。

insert()load()都接受指向第一本书的指针作为参数。这也是正确和正确的,因为这些方法为数组重新分配了内存。重新分配不一定就地进行-新空间可能与旧空间不在同一位置。调用之后,传递给realloc的原始指针必须被视为无效,并且在其位置使用的返回值(假设调用成功)。您也可以正确处理此问题,通过指针参数更新main的指针:

           *book = (Phonebook *)realloc(*book, sizeof(Phonebook) * (bookSize));

但是您尝试将电话簿条目写入分配的空间是不正确的。例如,在load()中,此:

           strcpy(book[i]->name, temp);

尝试访问Phonebook *指向的数组指针中的i th book,并将其写入namePhonebook成员它指出。但是,只有一个Phonebook *,而不是它们的数组。您正在为其指向的Phonebook分配和重新分配空间。

下面是简图:


实际布局

[Phonebook **]  ----> [Phonebook *]  ----> [ Phonebook, Phonebook, Phonebook ... ]

正在访问,就像是在访问

[Phonebook **]  ----> [Phonebook *, Phonebook *, Phonebook *, ...]
                           |            |            |
                           V            |            |
                      [Phonebook]       V            |
                                   [Phonebook]       V
                                                 [Phonebook]

解决方案:

就像您将分配的指针分配给*book,而不是分配给book一样,应该将索引运算符应用于*book

            strcpy((*book)[i].name, temp);

由于它是Phonebook的数组,而不是指向它们的指针的数组,因此,如图所示,您使用直接成员访问运算符(.),而不是间接访问运算符。

但是请注意,在不同的函数中使用相同的名称book来指定具有不同间接度的指针。因此,尽管上述内容在load()insert()中是正确的,但在main()和其他一些功能中却是错误的。


2
投票

tl; dr:insert(&book)应该只是insert(book),并将其定义为在堆中分配内存以存储从calloc获得的地址而获得的地址。

您将insert()的自变量定义为**book,并且当从*book调用中获得calloc()时,可以合理地使用地址运算符*“添加另一个&”。要注意的是,从calloc调用中获得的*book的地址是main()函数的调用堆栈上的一个位置。因此,当strcpy()的参数使用数组索引符号解引用该地址时,它将尝试获取位于调用堆栈+ bookSize - 1上的指针处的值。这已经处于未定义的行为范围内,因为堆栈不应该动态存储内存,但是由于堆栈位于内存布局的顶部(高地址区域),因此您会遇到段错误,因此请添加足够大的值到book的取消引用值会使您进入非法的内存访问区。

© www.soinside.com 2019 - 2024. All rights reserved.