【问题标题】:(C) Getting segmentation fault on pointer strcpy(C) 指针 strcpy 出现分段错误
【发布时间】:2020-09-26 23:12:40
【问题描述】:

我是 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)

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

【问题讨论】:

  • #includes、PhonebookbookSize 都不见了。请发布真实的、可编译的代码。
  • struct template of Phonebook 在哪里? bookSize 是什么?你不认为这是代码的相关部分吗?
  • 我已将整个代码添加到问题中。感谢提醒我发布可编译的代码!
  • 如果您使用的是 Linux,您可以下载并使用 Valgrind 程序。它对于调试段错误之类的东西非常方便。
  • 为了帮助 Valgrind,用-g 标志编译你的代码。这将导致编译器包含调试信息。

标签: c pointers struct strcpy


【解决方案1】:

正如您的其他答案所表明的那样,您正在绊倒双重间接的细节。

您将电话簿维护为一组结构。在main 中,变量book 是指向该数组中第一个结构的指针。第二个将立即在内存中跟随它,第三个将立即跟随它,etc.. 这一切都很好。

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

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

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

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

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

这是一个粗略的图表:


实际布局

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

就像被访问一样

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

解决方案:

正如您将分配的指针分配给*book,而不是book,您应该将索引运算符应用到*book

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

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

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

【讨论】:

  • 非常感谢!!您的解决方案解决了这个问题并且非常容易理解!我现在明白为什么我的代码出错了,以及你的解决方案是如何修复它的。感谢您为像我这样的初学者编写此解决方案所花费的时间和精力。
【解决方案2】:

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

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

【讨论】:

  • 您已正确诊断问题,但您的解决方案不可行。 OP 需要接收双指针,因为它们重新分配内存,并通过双指针返回新指针。
  • 感谢详细的解释!到目前为止,由于我对C缺乏了解,我无法完全理解你所说的。但我会确保彻底研究您的评论!
  • @JohnBollinger 似乎尼克的解决方案修复了段错误并且代码按预期运行。我错过了什么吗?
  • 是的,@seungyun。您错过了 Nick 的解决方案只有在 realloc() 将分配的内存扩展到位时才有效,这绝不是肯定的。
  • @NickSchmeller 抱歉,不确定您所说的“将获得的指针放在堆上并将地址传递给该指针以插入”是什么意思。将book 中的book地址(是的,在堆栈上)传递给insert 并没有错。实际上,如果insert 必须修改main 中的book 指针,则必须这样做,因为realloc。真正的问题是insert 使用错误的语法来访问数组中的最后一个元素。
猜你喜欢
  • 2018-02-17
  • 2013-11-18
  • 1970-01-01
  • 2014-10-21
  • 2013-02-23
  • 2020-02-18
  • 1970-01-01
  • 2019-05-10
  • 2018-03-17
相关资源
最近更新 更多