直接将fread()的返回值用作索引为何是不良编程实践?
嘿,我最近刚踩过这个坑——写了个读取.meta文件的函数,直接把fread()的返回值当数组索引用,结果被前辈狠狠批了一顿。今天就跟你掰扯掰扯,为啥这是个绝对不能碰的不良编程实践。
首先得搞明白fread()的返回值到底是什么:它返回的是成功读取的元素个数,只有当你指定每个元素大小为1字节时,它才等于读取的字节数。但就算是这种情况,直接拿它当索引用也藏着不少雷:
分不清“正常读完”和“读取出错”
当fread()返回的数值小于你请求读取的数量时,你根本没法直接判断是“真的读到文件末尾了”,还是“中途磁盘出错、读不下去了”。我之前就是直接用这个返回值给数组加终止符,前辈说:“要是磁盘读一半卡壳,你拿这个半截数据当完整的meta信息用,后面解析全乱套,出问题你都不知道是读取环节出的错!”类型不匹配容易踩溢出坑
fread()的返回值是size_t类型,这是个无符号整数。要是你数组的索引习惯用int这种有符号类型,一旦读取的字节数超过int的最大值,类型转换后会变成负数索引,直接触发数组越界访问——这可是程序崩溃、出现诡异行为的常客,查bug能查到你怀疑人生。隐含的未定义行为风险
就算你侥幸没遇到错误,看似正常的场景也可能出问题:比如你请求读取META_MAX字节,数组大小是META_MAX+1,刚好fread()返回META_MAX,这时候buffer[META_MAX] = '\0'是安全的,但如果fread()因为某些奇怪的系统问题返回了一个超过数组大小的值(虽然概率低,但不是不可能),直接当索引用就会越界,触发未定义行为。
那正确的姿势应该是啥样的?给你看我后来改的代码逻辑:
char buffer[META_MAX + 1]; size_t bytes_read = fread(buffer, 1, META_MAX, fp); // 先检查读取状态 if (bytes_read < META_MAX) { if (ferror(fp)) { // 处理读取错误,比如报错返回 perror("读取.meta文件失败"); return -1; } // 走到这里就是正常读到文件尾了 } // 确认没问题再给数组加终止符 buffer[bytes_read] = '\0';
先判断读取是否出错,确认没问题再用返回值操作数组,这样就把风险都堵上了。
备注:内容来源于stack exchange,提问作者Mio

