C语言读取txt文件unsigned int值出现异常偏移问题排查
故障核心原因
两个直接bug完全匹配你描述的故障现象:
- 最致命的错误:你将
char*类型的字符串返回值直接强转为unsigned int,这个操作不会把字符串里存储的数字文本转换成对应整数值,只会把字符串在内存中的首地址当作整数赋值给变量。同一次程序运行中,malloc分配的堆内存地址是连续线性的,所以所有读取结果的偏移量固定;开了地址随机化(ASLR)的系统上每次程序启动的堆基地址都不固定,所以不同次运行的偏移量完全随机。 fileFgets函数里的内存分配逻辑错误:malloc(strlen(tempStr) * sizeof(str))里sizeof(str)计算的是char指针的大小(32位系统是4字节、64位系统是8字节),不是char类型的1字节,且没有给字符串结尾的\0预留1字节空间,目前读字符串没出问题纯是内存布局的巧合,长文本场景会直接触发内存越界崩溃。
修复方案
你可以根据自己的代码习惯选下面任意一种方案:
方案1:修正通用逐行读取函数,配套正确的类型转换
先修复fileFgets的内存分配bug,增加错误处理:
#define MAX_STR_SIZE 1024 // 保持你原有宏定义即可 char* fileFgets(FILE* file) { char tempStr[MAX_STR_SIZE] = {0}; // 处理文件尾、读失败的边界情况 if (fgets(tempStr, MAX_STR_SIZE, file) == NULL) { return NULL; } // 同时兼容\n和Windows平台\r\n换行格式 tempStr[strcspn(tempStr, "\r\n")] = 0; // 正确计算分配长度:内容长度+1预留结束符位置,char类型大小固定为1 char* str = (char*)malloc(strlen(tempStr) + 1); if (str == NULL) return NULL; strcpy(str, tempStr); return str; }
绝对不能直接把char指针强转为整数类型*,读取无符号整数时,需要把返回的数字字符串用标准库函数解析为对应数值:
char* numLine = fileFgets(file); if (numLine == NULL) { // 自行补充读失败、到文件尾的处理逻辑 } unsigned int tempDuration; char* parseEnd = NULL; // 用strtoul做无符号数值解析,比atoi支持错误校验 unsigned long parseRes = strtoul(numLine, &parseEnd, 10); // 最后一个参数10表示按十进制解析 // 跳过解析末尾的空白符 while (isspace(*parseEnd)) parseEnd++; if (*parseEnd != '\0') { // 自行补充当前行内容不是合法无符号整数的异常处理 } tempDuration = (unsigned int)parseRes; free(numLine); // malloc申请的内存在用完后必须手动释放,避免内存泄漏
读取普通字符串时直接使用返回的char*即可,用完同样记得调用free释放。
方案2:拆分专用读取函数,降低误用概率
如果觉得统一读字符串再手动转换麻烦,可以分别实现字符串、无符号整数的专用读取函数,从接口层面避免指针强转的误用:
#include <ctype.h> // 用到isspace函数需要引入这个头文件 #define MAX_STR_SIZE 1024 // 专用逐行读字符串函数 char* readStringLine(FILE* file) { char tempStr[MAX_STR_SIZE] = {0}; if (fgets(tempStr, MAX_STR_SIZE, file) == NULL) return NULL; tempStr[strcspn(tempStr, "\r\n")] = 0; char* res = (char*)malloc(strlen(tempStr) + 1); if (res == NULL) return NULL; strcpy(res, tempStr); return res; } // 专用逐行读无符号整数函数,返回值0表示读取成功,非0为错误码 int readUintLine(FILE* file, unsigned int* outVal) { char tempStr[MAX_STR_SIZE] = {0}; if (fgets(tempStr, MAX_STR_SIZE, file) == NULL) return -1; char* parseEnd = NULL; unsigned long parseRes = strtoul(tempStr, &parseEnd, 10); while (isspace(*parseEnd)) parseEnd++; if (*parseEnd != '\0') return -2; *outVal = (unsigned int)parseRes; return 0; }
调用时直接对应数据类型选择函数即可:
// 读字符串 char* content = readStringLine(file); // 用完后free(content) // 读无符号整数 unsigned int tempDuration; int readRet = readUintLine(file, &tempDuration); if (readRet != 0) { // 自行补充读失败、内容非法的处理逻辑 }
内容的提问来源于stack exchange,提问作者Yoav Raman
相关产品推荐
相关产品推荐

