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

C++从字节缓冲区读取无符号整数时高位丢失的问题及优化

问题:C++小端序字节读取错误分析与优化

中断编程20余年后回归,开始学习C++(此前几乎未使用过该语言,曾是Object Pascal开发者),目前是C++纯新手。尝试编写程序将整个文件读取到字节流中,再从中读取2字节或4字节的无符号小端序数值,遇到了结果不符合预期的问题。

全局变量定义

std::string File_Content = "";  // 存储整个文件内容的字节数组
unsigned __int32 File_Pos = 0;  // File_Content中的当前位置,下一个要读取的字节位置

最初的读取函数实现

通过引用返回值避免无意义拷贝:

static __int8 Read2Bytes(unsigned __int32 iPos, unsigned __int8 iEndian, unsigned __int16 &iReturn)
{
   if (iEndian == 0)  // 小端序
   { 
      // 将第二个字节移到高位
      iReturn = File_Content[iPos] | (File_Content[iPos + 1] << 8);
   }  // end if (iEndian == 0)
   else
   {  // 大端序,暂时不重要
   }  // end else (iEndian == 0)

   File_Pos += 2;  // 移动位置标记
   return 0;
}  // end Read2Bytes

函数调用方式

unsigned __int16 iTimeStamp = 0;      // 2字节变量
Read2Bytes(File_Pos, 0, iTimeStamp);  // 参数:文件流位置、字节序、返回值引用

问题现象

根据WinHex显示,字节流中的目标字节为0xC6 0x9D,预期小端序转换后得到0x9DC6,但实际返回0xFFC6,读取4字节的函数也存在相同问题。

临时变量修复后的代码

改用临时变量存储字节后组合,问题解决:

static __int8 Read2Bytes(unsigned __int32 iPos, unsigned __int8 iEndian, unsigned __int16 &iReturn)
{
   unsigned __int8 bHiByte, bLoByte;  // 分别存储两个字节

   if (iEndian == 0)  // 小端序
   { 
      // 原错误版本
      //iReturn = File_Content[iPos] | (File_Content[iPos + 1] << 8);

      // 新尝试:逐字节读取后组合
      bLoByte = File_Content[iPos];       // 读取第一个字节
      bHiByte = File_Content[iPos + 1];   // 读取第二个字节
      iReturn = (bHiByte << 8) | bLoByte; // 按字节序组合

   }  // end if (iEndian == 0)

   // 其余代码不变
}

错误原因分析

问题核心在于std::string的元素类型:std::string的operator[]返回的是char类型,而多数系统中默认char是有符号类型。

当读取值为0x9D的字节时,作为有符号char会被解释为-99(0x9D是负数的补码表示)。执行File_Content[iPos + 1] << 8时,该有符号char会被整数提升为int类型,提升过程中会进行符号扩展——高位填充1,导致0x9D变成0xFFFFFF9D。左移8位后得到0xFFFF9D00,再和0xC6按位或,最终结果为0xFFFF9DC6,而iReturn是unsigned __int16,截断后就变成了0xFFC6,与实际看到的结果一致。

用unsigned __int8临时变量存储时,赋值过程会把有符号char的符号位当作普通位处理,避免了符号扩展,因此结果正确。


高效实现方案(无需临时变量)

只需要在读取std::string元素时,显式转换为unsigned char,就能阻止符号扩展:

static __int8 Read2Bytes(unsigned __int32 iPos, unsigned __int8 iEndian, unsigned __int16 &iReturn)
{
   if (iEndian == 0)  // 小端序
   { 
      iReturn = static_cast<unsigned char>(File_Content[iPos]) | 
                (static_cast<unsigned char>(File_Content[iPos + 1]) << 8);
   }
   else
   {
      // 大端序处理(如果需要)
      iReturn = (static_cast<unsigned char>(File_Content[iPos]) << 8) | 
                static_cast<unsigned char>(File_Content[iPos + 1]);
   }

   File_Pos += 2;
   return 0;
}

更优的字节流存储方式

直接用std::vector<unsigned char>存储文件内容,从根源上避免char的符号问题,同时语义更清晰(明确是字节流而非字符串):

#include <fstream>
#include <vector>

std::vector<unsigned char> File_Content;
unsigned __int32 File_Pos = 0;

// 读取文件到vector的示例代码
void LoadFile(const std::string& filename)
{
    std::ifstream file(filename, std::ios::binary | std::ios::ate);
    if (!file) return;

    std::streamsize size = file.tellg();
    file.seekg(0, std::ios::beg);

    File_Content.resize(size);
    file.read(reinterpret_cast<char*>(File_Content.data()), size);
}

// 简化后的读取函数,无需类型转换
static __int8 Read2Bytes(unsigned __int32 iPos, unsigned __int8 iEndian, unsigned __int16 &iReturn)
{
   if (iEndian == 0)
   { 
      iReturn = File_Content[iPos] | (File_Content[iPos + 1] << 8);
   }
   else
   {
      iReturn = (File_Content[iPos] << 8) | File_Content[iPos + 1];
   }

   File_Pos += 2;
   return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 22:29:56