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

C语言读取文件最后n行时重复打印最后一行的问题求助

问题:C语言读取文件最后n行时重复打印最后一行

我尝试用C语言读取文件的最后n行并打印,使用fgets()读取行内容的部分似乎正常,但打印存储在数组中的最后n行时,只会重复输出最后一行,应该是存储字符串的方式存在问题。

原代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int agrc, char** agrv) {
    FILE* input;
    input = fopen(agrv[1], "r");

    int n = *agrv[2]-'0';
    int line = 0;
    char text[11];
    char** tab = malloc(1000000*sizeof(text));

    while(fgets(text, sizeof(text), input) != 0) {
        tab[line] = text;
        line++;
    }

    fclose(input);

    int jump = line-n;
    
    for(int i=jump; i<line; i++) {
        printf("%s", tab[i]);
    }
}

编辑后尝试修改循环,但问题仍然存在:

while(fgets(text, sizeof(text), input) != 0) {
    char text2[11];
    strcpy(text2, text);
    tab[line] = text2;
    line++;
}

问题原因

  1. 原代码的错误:
    你把tab[line]指向了栈上的固定数组text,每次调用fgets都会覆盖text里的内容。数组tab中所有元素最终都指向同一个内存地址,打印时自然全是最后一行的内容。

  2. 编辑后代码的错误:
    循环内定义的text2是局部栈变量,每次循环结束后,该变量的内存会被系统回收或复用。tab中存储的都是无效的栈地址,后续访问属于未定义行为,结果依然错误。


修正方案

每次读取一行后,需要为该行内容分配堆内存,保证每行的内容都有独立的存储空间。可以用malloc配合strcpy,或者使用POSIX标准函数strdup(注意部分环境可能需要额外支持)。

修正后的完整代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char** argv) {
    // 参数检查
    if (argc != 3) {
        fprintf(stderr, "用法: %s <文件名> <行数n>\n", argv[0]);
        return 1;
    }

    // 打开文件
    FILE* input = fopen(argv[1], "r");
    if (!input) {
        perror("打开文件失败");
        return 1;
    }

    // 转换行数n,支持多位数
    int n = atoi(argv[2]);
    if (n <= 0) {
        fprintf(stderr, "n必须是正整数\n");
        fclose(input);
        return 1;
    }

    int line = 0;
    char text[11];
    // 分配存储指针的数组,每个元素是char*类型
    char** tab = malloc(1000000 * sizeof(char*));
    if (!tab) {
        perror("内存分配失败");
        fclose(input);
        return 1;
    }

    while (fgets(text, sizeof(text), input) != NULL) {
        // 为当前行分配内存,长度为字符串长度+1(存终止符)
        tab[line] = malloc(strlen(text) + 1);
        if (!tab[line]) {
            perror("行内存分配失败");
            // 释放已分配的内存,避免泄漏
            for (int i = 0; i < line; i++) {
                free(tab[i]);
            }
            free(tab);
            fclose(input);
            return 1;
        }
        // 复制行内容到堆内存
        strcpy(tab[line], text);
        line++;
    }

    fclose(input);

    // 处理文件行数小于n的情况,从第一行开始打印
    int jump = line - n;
    if (jump < 0) jump = 0;

    // 打印最后n行并释放内存
    for (int i = jump; i < line; i++) {
        printf("%s", tab[i]);
        free(tab[i]);
    }
    free(tab);

    return 0;
}

额外说明

  • 原代码中malloc(1000000*sizeof(text))是错误的,tab是char**类型,每个元素是char*,因此应该用sizeof(char*)计算分配大小。
  • 使用atoi转换行数n比直接*agrv[2]-'0'更健壮,支持两位数及以上的n。
  • 增加了参数检查、文件打开检查、内存分配检查,避免程序崩溃。
  • 用完内存后及时释放,避免内存泄漏。
  • 处理了文件总行数小于n的边界情况,此时会打印所有行。

内容的提问来源于stack exchange,提问作者TCL

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 14:36:16