(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)
** 抱歉删除了我认为是 'irrelevant' 的部分代码!
我已将整个代码添加到 post。谢谢!
tl;dr:insert(&book)
应该只是 insert(book)
,并将其定义为您从堆中分配内存获得的地址,用于存储您从 calloc
获得的地址.
您将 insert()
的参数定义为 **book
,当您从 calloc()
调用中得到 *book
时,您合理地 "add on another *
"地址运算符 &
。要注意的是,您从 calloc 调用中获得的 *book
地址是 main()
函数调用堆栈上的一个位置。因此,当 strcpy()
的参数使用数组索引符号取消引用此地址时,它会尝试获取位于调用堆栈 + bookSize - 1
上的指针处的值。这已经在未定义的行为领域,因为堆栈不应该动态存储内存,但是你会得到段错误,因为堆栈位于内存布局的顶部(高地址区域),所以添加足够大的值book
的取消引用值会将您置于非法内存访问区域。
正如您的其他回答所表明的,您被双重间接寻址的细节绊倒了。
您正在维护您的 phone 书籍作为结构数组。在 main
中,变量 book
是指向该数组中第一个结构的指针。第二个将在内存中紧随其后,第三个将紧随其后,等..这一切都很好。
insert()
和 load()
都接受指向第一本书的指针作为参数。这也是天经地义的,因为这些方法为数组重新分配了内存。重新分配不一定就地完成——新的 space 可能与旧的位于不同的位置。传递给 realloc
的原始指针在调用后必须被视为无效,并在其位置使用 return 值(假设调用成功)。您也正确处理了这个问题,通过指针参数更新了 main
的指针:
*book = (Phonebook *)realloc(*book, sizeof(Phonebook) * (bookSize));
但是您尝试将 phone 书籍条目写入分配的 space 是不正确的。例如,在 load()
中,这个:
strcpy(book[i]->name, temp);
尝试访问指针数组中的ithPhonebook *
book
指向,并写入它指向的 Phonebook
的 name
成员。但是只有一个 Phonebook *
,而不是它们的数组。您正在为它指向的 Phonebook
分配和重新分配 space。
这是一个粗略的图表:
实际布局:
[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()
和其他一些函数中却是错误的。
我是 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)
** 抱歉删除了我认为是 'irrelevant' 的部分代码!
我已将整个代码添加到 post。谢谢!
tl;dr:insert(&book)
应该只是 insert(book)
,并将其定义为您从堆中分配内存获得的地址,用于存储您从 calloc
获得的地址.
您将 insert()
的参数定义为 **book
,当您从 calloc()
调用中得到 *book
时,您合理地 "add on another *
"地址运算符 &
。要注意的是,您从 calloc 调用中获得的 *book
地址是 main()
函数调用堆栈上的一个位置。因此,当 strcpy()
的参数使用数组索引符号取消引用此地址时,它会尝试获取位于调用堆栈 + bookSize - 1
上的指针处的值。这已经在未定义的行为领域,因为堆栈不应该动态存储内存,但是你会得到段错误,因为堆栈位于内存布局的顶部(高地址区域),所以添加足够大的值book
的取消引用值会将您置于非法内存访问区域。
正如您的其他回答所表明的,您被双重间接寻址的细节绊倒了。
您正在维护您的 phone 书籍作为结构数组。在 main
中,变量 book
是指向该数组中第一个结构的指针。第二个将在内存中紧随其后,第三个将紧随其后,等..这一切都很好。
insert()
和 load()
都接受指向第一本书的指针作为参数。这也是天经地义的,因为这些方法为数组重新分配了内存。重新分配不一定就地完成——新的 space 可能与旧的位于不同的位置。传递给 realloc
的原始指针在调用后必须被视为无效,并在其位置使用 return 值(假设调用成功)。您也正确处理了这个问题,通过指针参数更新了 main
的指针:
*book = (Phonebook *)realloc(*book, sizeof(Phonebook) * (bookSize));
但是您尝试将 phone 书籍条目写入分配的 space 是不正确的。例如,在 load()
中,这个:
strcpy(book[i]->name, temp);
尝试访问指针数组中的ithPhonebook *
book
指向,并写入它指向的 Phonebook
的 name
成员。但是只有一个 Phonebook *
,而不是它们的数组。您正在为它指向的 Phonebook
分配和重新分配 space。
这是一个粗略的图表:
实际布局:
[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()
和其他一些函数中却是错误的。