簡體   English   中英

(C) 指針 strcpy 出現分段錯誤

[英](C) Getting segmentation fault on pointer strcpy

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

** 抱歉刪除了我認為“不相關”的部分代碼!
我已將整個代碼添加到帖子中。 謝謝!

tl; dr: insert(&book)應該只是insert(book) ,並將其定義為您通過在堆中分配 memory 獲得的地址,以存儲您從calloc獲得的地址。

您將insert()的參數定義為**book ,並且當您從calloc()調用中獲得*book時,您可以合理地使用地址運算符& “添加另一個* ”。 問題是您從 calloc 調用中獲得的*book地址是main() function 的調用堆棧上的一個位置。 因此,當strcpy()的參數使用數組索引表示法取消引用此地址時,它會嘗試獲取位於調用堆棧 + bookSize - 1上的指針處的值。 這已經處於未定義的行為領域,因為堆棧不應該動態存儲 memory,但是您會遇到段錯誤,因為堆棧位於 memory 布局(高地址區域)的頂部,因此添加足夠大的值book的取消引用值會將您置於非法的 memory 訪問區域。

正如您的其他答案所表明的那樣,您正在絆倒雙重間接的細節。

您將電話簿維護為一組結構。 main中,變量book是指向該數組中第一個結構的指針。 第二個將立即在memory中緊隨其后,第三個將緊隨其后,依此類推.. 這一切都很好。

insert()load()都接受一個指向第一本書的指針作為參數。 這也是正確的,因為這些方法為數組重新分配了 memory。 重新分配不一定就地完成——新空間可能與舊空間位於不同的位置。 傳遞給realloc的原始指針在調用后必須被認為是無效的,並在其位置使用返回值(假設調用成功)。 您也可以正確處理此問題,通過指針參數更新main的指針:

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

但是您嘗試將電話簿條目寫入分配的空間是不正確的。 例如,在load()中,這是:

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

嘗試訪問book指向的指針數組中的第iPhonebook * ,並寫入它指向的Phonebookname成員。 但是只有一個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()和其他一些函數中是錯誤的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM