移除fgets缓冲区中的'\n'导致C程序崩溃的原因排查
问题描述
尝试从TXT文件导入一组Libro结构体,文件中每个条目按以下结构排列:
titolo editore prezzo anno
使用fgets读取字符串时,尝试删除末尾的'\n'会导致程序无法正常运行;如果保留换行符,程序能运行但数据会带换行。试过for循环、strcspn等方法都无效,相关代码如下:
获取Libro数组的函数代码
#include "libro.h" #include <string.h> #include <stdlib.h> #include <stdio.h> Libro* get_biblioteca(char path[], int size){ char buff[255]; char titolo[255]; char editore[255]; float prezzo; int curr = 0; int c_lib = 0; Libro *biblioteca = malloc(sizeof(Libro)*size); FILE *fp = fopen(path, "r"); while (fgets(buff, 255, fp)!=NULL){ int size = strnlen(buff, 255); if (buff[size-1]=='\n') { buff[size-1]='\0'; //<---- 删掉这行程序能运行,但会保留\n printf(">%s<", buff); } if (curr == 0) strcpy(titolo, buff); if (curr == 1) strcpy(editore, buff); if (curr == 2) prezzo = atof(buff); if (curr == 3){ biblioteca[c_lib]=crea_libro(titolo, editore, prezzo, atoi(buff)); c_lib++; curr = 0; } else curr++; } return biblioteca; } int main(void){ Libro *biblio = get_biblioteca("test.txt", 5); print_libro(biblio[3]); }
头文件libro.h内容
typedef struct opaque_libro *Libro; Libro crea_libro (char titolo[], char editore[], float prezzo, int anno);
libro.c内容
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "libro.h" typedef struct opaque_libro { char *titolo; char *editore; float prezzo; int anno; } opaque_libro; Libro crea_libro (char titolo[], char editore[], float prezzo, int anno){ int size = strlen(titolo); Libro new = malloc(sizeof(Libro)); if (new == NULL) return NULL; new->titolo = malloc(size * sizeof(char)); if (new->titolo == NULL) return NULL; size = strlen(editore); new ->editore = malloc(size * sizeof(char)); if (new->editore == NULL) return NULL; new->prezzo = prezzo; new->anno = anno; strcpy(new->titolo, titolo); strcpy(new->editore, editore); return new; } void print_libro (Libro lib){ printf("titolo: %s\neditore: %s\nprezzo: %.2f\nanno: %d\n", lib->titolo, lib->editore, lib->prezzo, lib->anno); }
测试文件test.txt内容
Up Down 20.5 1999 Gran Canyon 22 1998 Junji Ito 24 1864 Current Emergencies 1.20 2024 Left Down 90 2024
问题分析与解决方案
程序出错的核心原因不在换行符的处理,而是内存分配的两处致命错误,删除换行符后暴露了这些内存问题:
1. crea_libro中结构体内存分配错误
Libro是struct opaque_libro *类型的指针,malloc(sizeof(Libro))只分配了一个指针的内存空间,而非整个结构体的大小。这会导致后续访问结构体成员时内存越界,行为不可预测。
修正:改为分配struct opaque_libro的大小:
Libro new = malloc(sizeof(opaque_libro));
2. 字符串内存分配未预留结束符空间
strlen(titolo)返回的是字符串不含'\0'的长度,malloc(size * sizeof(char))没有为字符串结束符'\0'预留空间,strcpy时会写入超出分配的内存,破坏其他数据。当保留换行符时,字符串长度多1,这个问题可能刚好没触发明显错误;删除换行符后长度变化,内存越界的影响显现出来。
修正:分配内存时多留1个字节给'\0':
new->titolo = malloc((size + 1) * sizeof(char)); // ... new->editore = malloc((size + 1) * sizeof(char));
3. 其他优化点
get_biblioteca中变量名size与函数参数重名,建议改为buff_len避免混淆;- 增加文件打开失败的检查,防止空指针访问;
- 可以用更简洁的方式去除换行符:
buff[strcspn(buff, "\n")] = '\0';,无需手动计算长度。
修正后的完整代码
修正后的libro.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "libro.h" typedef struct opaque_libro { char *titolo; char *editore; float prezzo; int anno; } opaque_libro; Libro crea_libro (char titolo[], char editore[], float prezzo, int anno){ int size = strlen(titolo); // 分配结构体大小而非指针大小 Libro new = malloc(sizeof(opaque_libro)); if (new == NULL) return NULL; // 为字符串结束符预留空间 new->titolo = malloc((size + 1) * sizeof(char)); if (new->titolo == NULL) { free(new); return NULL; } size = strlen(editore); new->editore = malloc((size + 1) * sizeof(char)); if (new->editore == NULL) { free(new->titolo); free(new); return NULL; } new->prezzo = prezzo; new->anno = anno; strcpy(new->titolo, titolo); strcpy(new->editore, editore); return new; } void print_libro (Libro lib){ printf("titolo: %s\neditore: %s\nprezzo: %.2f\nanno: %d\n", lib->titolo, lib->editore, lib->prezzo, lib->anno); }
修正后的get_biblioteca函数
#include "libro.h" #include <string.h> #include <stdlib.h> #include <stdio.h> Libro* get_biblioteca(char path[], int size){ char buff[255]; char titolo[255]; char editore[255]; float prezzo; int curr = 0; int c_lib = 0; Libro *biblioteca = malloc(sizeof(Libro)*size); if (!biblioteca) return NULL; FILE *fp = fopen(path, "r"); if (!fp) { free(biblioteca); return NULL; } while (fgets(buff, 255, fp)!=NULL){ // 简洁去除换行符 buff[strcspn(buff, "\n")] = '\0'; printf(">%s<\n", buff); switch(curr) { case 0: strcpy(titolo, buff); break; case 1: strcpy(editore, buff); break; case 2: prezzo = atof(buff); break; case 3: biblioteca[c_lib] = crea_libro(titolo, editore, prezzo, atoi(buff)); c_lib++; curr = -1; // 后续curr++后回到0 break; } curr++; } fclose(fp); return biblioteca; } int main(void){ Libro *biblio = get_biblioteca("test.txt", 5); if (biblio) { print_libro(biblio[3]); // 记得后续要释放内存,避免泄漏 for(int i=0; i<5; i++) { free(biblio[i]->titolo); free(biblio[i]->editore); free(biblio[i]); } free(biblio); } return 0; }
内容的提问来源于stack exchange,提问作者Maridiama
相关产品推荐
相关产品推荐

