C语言程序向stdout输出异常字符的原因排查与解决
问题原因
核心问题是**fwrite参数顺序完全错误**,加上依赖fseek+ftell获取文件大小的方式存在兼容性缺陷:
fwrite参数误用:fwrite的标准原型是:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
参数依次为:待写入的数据缓冲区指针、单个元素字节数、元素数量、目标输出流。
你的代码写的是fwrite(stdout, 1, sz, temp);,相当于把stdout这个FILE*结构体的内存数据当成输出内容,试图写入temp流——完全搞反了读写逻辑,这直接导致输出异常乱码。
- 文本模式下的文件大小问题:
你用"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
相关产品推荐
相关产品推荐

