You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

移除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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 06:27:32