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

C语言程序向stdout输出异常字符的原因排查与解决

问题原因

核心问题是**fwrite参数顺序完全错误**,加上依赖fseek+ftell获取文件大小的方式存在兼容性缺陷:

  1. fwrite参数误用:
    fwrite的标准原型是:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

参数依次为:待写入的数据缓冲区指针、单个元素字节数、元素数量、目标输出流。
你的代码写的是fwrite(stdout, 1, sz, temp);,相当于把stdout这个FILE*结构体的内存数据当成输出内容,试图写入temp流——完全搞反了读写逻辑,这直接导致输出异常乱码。

  1. 文本模式下的文件大小问题:
    你用"r"(默认文本模式)打开文件,而C标准不保证fseek(temp, 0L, SEEK_END)在文本模式下的行为是定义明确的,ftell返回的也不是准确的字节数(而是字符计数),这会导致获取的文件大小偏差,进一步加剧输出异常。
解决方法

推荐使用循环缓冲区读写的方式(适配所有文件类型,无内存溢出风险),代码修正如下:

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

void concatenate(int argc, char *argv[]);

int main(int argc, char *argv[]) {
    concatenate(argc, argv);
    return 0;
}

void concatenate(int argc, char *argv[]) {
    FILE *temp;
    char buf[4096]; // 4KB缓冲区,可按需调整
    size_t read_bytes;

    for (int i = 1; i < argc; i++) {
        // 用二进制模式打开,避免文本模式的指针操作问题
        if ((temp = fopen(argv[i], "rb")) == NULL) {
            fprintf(stderr, "Error while opening file %s\n", argv[i]);
            exit(EXIT_FAILURE);
        }

        // 循环读取并写入,直到文件结束
        while ((read_bytes = fread(buf, 1, sizeof(buf), temp)) > 0) {
            fwrite(buf, 1, read_bytes, stdout);
        }

        // 检查读取过程是否出错
        if (ferror(temp)) {
            fprintf(stderr, "Error reading file %s\n", argv[i]);
            fclose(temp);
            exit(EXIT_FAILURE);
        }

        fclose(temp);
    }
}

如果坚持要一次性读取文件内容,需修正参数并修复文件大小获取逻辑(仅适合小文件):

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

int concatenate(int argc, char *argv[]);

int main(int argc, char *argv[]) {
    concatenate(argc, argv);
    return 0;
}

int concatenate(int argc, char *argv[]) {
    FILE *temp;
    long sz;
    char *buf;

    for (int i = 1; i < argc; i++) {
        if ((temp = fopen(argv[i], "rb")) == NULL) {
            fprintf(stderr, "Error while opening file %s\n", argv[i]);
            exit(EXIT_FAILURE);
        }

        if (fseek(temp, 0L, SEEK_END) != 0) {
            fprintf(stderr, "Error seeking file %s\n", argv[i]);
            fclose(temp);
            exit(EXIT_FAILURE);
        }

        sz = ftell(temp);
        if (sz == -1) {
            fprintf(stderr, "Error getting size of file %s\n", argv[i]);
            fclose(temp);
            exit(EXIT_FAILURE);
        }
        rewind(temp);

        buf = malloc(sz);
        if (buf == NULL) {
            fprintf(stderr, "Memory allocation failed\n");
            fclose(temp);
            exit(EXIT_FAILURE);
        }

        size_t read = fread(buf, 1, sz, temp);
        if (read != sz) {
            fprintf(stderr, "Error reading file %s\n", argv[i]);
            free(buf);
            fclose(temp);
            exit(EXIT_FAILURE);
        }

        fwrite(buf, 1, sz, stdout);

        free(buf);
        fclose(temp);
    }
    return 0;
}
避免未来踩坑的建议
  • 牢记标准库函数参数:用fwrite/fread前,通过man fwrite(Linux)或C标准文档确认参数顺序,不要凭记忆写。
  • 优先循环读写:一次性读取需要分配与文件等大的内存,大文件会导致OOM,循环读写更高效安全。
  • 区分文件打开模式:涉及文件指针操作(fseek/ftell)时,用二进制模式(rb/wb)避免未定义行为。
  • 全链路错误检查:对fopen、malloc、fread等可能失败的操作,必须添加错误处理,避免静默异常。
  • 测试边界场景:验证空文件、大文件、含特殊字符的文件,确保程序鲁棒性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 03:34:57