![](/img/trans.png)
[英]My program crashes when I try to read a NULL value using sqlite in C
[英]why when i put a proper value in semester variable my program crashes? C
struct student{
char *number;
char *name;
int semester;
float *grades;
};
typedef struct student RECORD;
void read_record(RECORD *p);
void print_record(RECORD x);
void init_record(RECORD *p);
void free_record(RECORD x);
main()
{
int N;
printf("Please enter the number of students that you want to insert: ");
scanf("%d",&N);
RECORD array[N];
int i;
for (i=0; i<N; i++)
init_record(&array[i]);
此時,當我從鍵盤輸入學期值時,我的程序崩潰了
for (i=0; i<N; i++)
{
printf("Number %d student: \n",i+1);
read_record(&array[i]);
}
for (i=0; i<N; i++)
print_record(array[i]);
for (i=0; i<N; i++)
{
free_record(array[i]);
}
}
我試圖刪除任何不必要的代碼,但我留下了最重要的部分,因此任何錯誤的部分都更容易被看到
void read_record(RECORD *p)
{
int i;
printf("Please give the name: ");
scanf("%s", p->name);
printf("Please give AM number: ");
scanf("%s", p->number);
printf("Please give semester: ");
scanf("%d",p->semester);
printf("Please give grades: ");
for(i=0;i<5;i++)
scanf("%f", &(p->grades[i]));
}
在這段代碼之后,我有init_record
函數,其中包括結構數組的 malloc
void init_record(RECORD *p)
{
p->name = malloc(sizeof(char)*40);
if (!p->name)
{
printf("Error!");
exit(0);
}
p->number = malloc(sizeof(char)*6);
if (!p->number)
{
printf("Error!");
exit(0);
}
p->grades = malloc(sizeof(float)*5);
if (!p->grades)
{
printf("Error!");
exit(0);
}
}
假設您已經為結構成員分配了內存
printf("Please give semester: ");
scanf("%d",p->semester);
是問題所在,因為p->semester
是一個int
。 %d
需要一個指向int
的指針,您將一個未初始化的整數值作為指向scanf
的指針傳遞。
正確的調用是
scanf("%d", &p->semester);
通常一次使用malloc
是因為需要存在於函數范圍之外的對象,例如鏈表、樹等。但是當您需要一個在編譯器時維度未知的數組時也是如此。
您的init_record
在我看來毫無意義,因為您知道數組的大小,而且它們甚至很小,您可以輕松地將結構更改為
struct student {
char number[6];
char name[40];
int semester;
float grades[5];
};
然后你就不需要做malloc
調用了。
此外,您需要像這樣執行scanf
:
國際 c;
scanf("%39s", p->name);
while((c = getchar()) != '\n' && c != EOF); // clearing the buffer
scanf("%5s", p->number);
while((c = getchar()) != '\n' && c != EOF); // clearing the buffer
...
如果用戶輸入的名稱/號碼對於緩沖區來說太大,則可以防止緩沖區溢出。
編輯
OP 在評論中詢問
最后一兩件事:我想替換scanf函數與
gets
的名字的情況下,因為我想一個名字和姓氏保存為一個整體(包括空格)。 但是當我替換 scanf 時,我收到了這個錯誤:在不是結構或聯合的東西中請求成員“名稱”。 我在這里搜索了類似的問題,但沒有找到任何解決方案。 你能推薦一個嗎?
不要使用gets
,這是一個危險的函數,因為它沒有考慮數組的大小,如果輸入的文本大於緩沖區大小,它會溢出並造成很大的損壞。 錯誤消息是由於語法錯誤造成的,因為您沒有顯示代碼,我無法說出您做錯了什么。 但是,如果您接受我的建議,就不會出現此錯誤。
一般來說,我建議不要使用scanf
來讀取用戶的信息,因為scanf
不是為了這樣做而設計的。 特別是當你想讀取有空格的字符串時,最好用fgets
讀取整行,然后使用其他函數解析該行,如strchr
、 strstr
、 strtok
、 sscanf
、 strtol
等。使用哪個函數取決於你正在嘗試從用戶那里讀取。 在這種情況下,您正在讀取字符串, fgets
會給出更好的結果。 所以我會把你的整個閱讀過程改成這樣:
int read_record(RECORD *p)
{
char line[1024];
printf("Please give the name: ");
if(fgets(line, sizeof line, stdin) == NULL)
{
fprintf(stderr, "Could not read name\n");
return 0; // error
}
line[strcspn(line, "\n")] = 0; // removing newline
strncpy(p->name, line, sizeof p->name);
p->name[sizeof(p->name) - 1] = 0; // making sure to get a valid string
printf("Please give AM number: ");
if(fgets(line, sizeof line, stdin) == NULL)
{
fprintf(stderr, "Could not read AM number\n");
return 0;
}
line[strcspn(line, "\n")] = 0; // removing newline
strncpy(p->number, line, sizeof p->name);
p->name[sizeof(p->number) - 1] = 0; // making sure to get a valid string
// this is ok, this can stay like this
printf("Please give semester: ");
scanf("%d", &p->semester);
printf("Please give grades: ");
if(fgets(line, sizeof line, stdin) == NULL)
{
fprintf(stderr, "Could not read grades\n");
return 0;
}
if(sscanf(line, "%f %f %f %f %f",
p->grades, p->grades + 1, p->grades + 2, p->grades + 3,
p->grades + 4) != 5)
{
fprintf(stderr, "Could not read 5 grades\n");
return 0;
}
return 1; // success
}
我知道您以前擁有的代碼要多得多,但此代碼更健壯,它可以處理用戶輸入錯誤格式的情況,您的代碼可以對此做出反應,打印錯誤消息,重試用戶輸入,等等。 當屏幕或文件上打印的數據很奇怪時,錯誤就會顯現出來。
請注意我是如何復制字符串的:
strncpy(p->name, line, sizeof p->name);
p->name[sizeof(p->name) - 1] = 0; // making sure to get a valid string
在這里,我假設您已經更改了結構以保存數組,就像我在答案的第一部分中所說的那樣。 如果您沒有更改它並且您仍然通過使用硬編碼固定大小的malloc
使用舊方法,則您也必須在此處使用硬編碼固定大小:
strncpy(p->name, line, 40);
p->name[39] = 0; // making sure to get a valid string
並且您明白為什么我更喜歡結構具有數組時的原因,因為使用sizeof
我可以獲得大小而不管維度如何。
這里要注意的另一件事是我使用了strncpy
而不是strcpy
。 strcpy
遇到與gets
相同的問題,它沒有考慮目標緩沖區的大小,如果源字符串大於目標緩沖區,它將溢出緩沖區。
strncpy
工作方式與strcpy
類似,只是您傳遞了目標緩沖區可用的字節數。 如果源大於該數字,則strncpy
將不會在目標中寫入字節,從而防止緩沖區溢出。 當然,如果'\\0'
終止字節不在復制的字節中,則不會寫入目標緩沖區。 p->name[39] = 0;
只需確保字符串以'\\0'
結尾,無論源有多長。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.