开发笔记之:文件读取值溢出bug分析(QT C++版)

文章讨论了在QTC++中使用QDataStream读取数据文件时遇到的EOF判断问题和无符号类型到带符号类型转换导致的溢出问题。通过三种对策,包括修改EOF检查方式、调整返回类型以及类型转换处理,解决了读取中断和提前结束的问题。强调了文件读取时注意EOF的正确判断和类型匹配的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(1)引言

 以下是QT C++读取数据文件(QDataStream)的代码:

/**
 * 按双字读取
 * @param fis						文件输入流
 * @param isBigEndian				是否大头(字节序)
 * @return				双字值
 */
DWORD FsFileUtil::readAsDword(QDataStream &fis, const bool isBigEndian) {
    BYTE byte1 = -1, byte2 = -1, byte3 = -1, byte4 = -1;
    DWORD val = -1;

    fis >> byte1 >> byte2 >> byte3 >> byte4;

    //EoF checking
    if((-1 == byte4)||(-1 == byte3)||(-1 == byte2)||(-1 == byte1)) {
        return (-1);
    }
    if(isBigEndian) {
        val = ((byte1 << 24)|(byte2 << 16)|(byte3 << 8)|byte4);
    } else {
        val = ((byte4 << 24)|(byte3 << 16)|(byte2 << 8)|byte1);
    }
    return (val);
}

 其意图很简单:从当前游标位置读取一个双字(4字节)数据。
稍微复杂一点的就是一个字节序的考虑。

(2)问题

 该代码做UT(单元测试)时,遇到了读取无法中断(就是读起来没完没了)。以下是数据文件内容:
数据文件sample中断的原因就是一直没有获取到约定的 EOF(-1)。
经调试跟踪,在读取完毕后,函数的返回值还是 4,294,967,2950xFFFFFF),没有返回 -1
经查,QDataSteam的 >> 即便读取到文件末尾了,也不会返回 -1,而需要使用方法 atEnd 来判定。

(2.1)对策一

将判断 EOF的语句修改为:

//EoF checking
if(fis.atEnd()) {
    return (-1);
}

再次调试,则判定文件末尾正常,但函数还是不会返回 -1。其原因是函数返回值类型是无符号型 DWORD
-1 的DWORD值是0xFFFFFF

(2.2)对策二

将函数的返回值类型扩展为带符号型 int64。即:

int64 FsFileUtil::readAsDword(QDataStream &fis, const bool isBigEndian) {
    BYTE byte1 = -1, byte2 = -1, byte3 = -1, byte4 = -1;
    int64 val = -1;

再次调试,遇到了读取中断的问题(就是文件还没读取完就提前中断了)。
经调试跟踪,在读取第1个 0xFFFFFF 时,每个字节的读取都正常(255),问题发生在代码的 return 语句处,返回 -1值了:

val = ((byte1 << 24)|(byte2 << 16)|(byte3 << 8)|byte4);

问题清楚了:无符号类型计算转换为带符号类型时,发生了值溢出。

(2.3)对策三

返回值val需要扩展,要不用带符号类型int64,在值计算时把值强制扩展为int64;
或者用无符号类型DWORD。即:

int64 FsFileUtil::readAsDword(QDataStream &fis, const bool isBigEndian) {
    BYTE byte1 = -1, byte2 = -1, byte3 = -1, byte4 = -1;
    int64 val = -1;
    。。。
    val = (((int64)byte1 << 24)|((int64)byte2 << 16)|((int64)byte3 << 8)|(int64)byte4);

或者:

int64 FsFileUtil::readAsDword(QDataStream &fis, const bool isBigEndian) {
    BYTE byte1 = -1, byte2 = -1, byte3 = -1, byte4 = -1;
    DWORD val = -1;
    。。。
    val = (((DWORD)byte1 << 24)|((DWORD)byte2 << 16)|((DWORD)byte3 << 8)|(DWORD)byte4);

相比之下,第1种方案的可读性要好一些。

(3)结论

数据文件读取需关注的点:

  1. 判定是否到达文件末尾(End of File)的方式
  2. 目标的值域范围(宁大勿小)
  3. 无符号类型与带符号类型之间的转换
(4)相关文档
  1. 开发笔记之:文件读取值溢出bug分析(JAVA版)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值