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

char与unsigned char位运算行为差异及字节合并异常原因解析

字节合并为uint16_t时的位运算异常问题

我尝试用位左移<<和按位或|操作将两个字节合并为uint16_t类型的16位数字,代码为uint16_t universe = (hb << 8) | lb;。但当低位字节lb的值大于0x80时,会出现异常结果(比如预期得到511,实际得到65535),而高位字节hb不受影响。

进一步测试发现:0x70 == '\x70'成立,但0x80 != '\x80'在部分场景下成立,且signed char与char在值>=0x80时处理方式不同。尽管char与unsigned char内存大小相同,但二者在位运算中的表现存在明显差异。

复现代码

#include <string>
#include <iostream>
using namespace std;

int main()
{
    // 结果不同的情况
    uint8_t hb = 0x01;
    uint8_t lb = 0xff;
    uint16_t universe = (hb << 8) | lb;
    cout<<universe << endl; // 输出511
    
    string line = string("\x01\xff");
    universe = (line[0] << 8) | line[1]; 
    cout<<universe << endl; // 输出65535
    
    // 另一组结果不同的情况
    hb = 0x10;
    lb = 0x80;
    universe = (hb << 8) | lb;
    cout<<universe << endl; // 输出4224
    
    line = string("\x10\x80");
    universe = (line.at(0) << 8) | line.at(1);
    cout<<universe << endl; // 输出65408
    
    universe = ('\x10' << 8) | '\x80';
    cout<<universe << endl; // 同样输出65408
    
    
    // 结果正常的情况
    hb = 0x83;
    lb = 0x70;
    universe = (hb << 8) | lb;
    cout<<universe << endl; // 输出33648
    
    line = string("\x83\x70");
    universe = (line[0] << 8) | line[1];
    cout<<universe << endl; // 输出33648
    
    
    // 仅当低位字节>0x80时出现异常
    if(0x70 == '\x70')
        cout << "same 0x70" << endl; // 输出same
        
    unsigned char nn = '\x80';
    if(0x80 == nn)
        cout << "now its same 0x80 again " << endl; // 输出same
    
    uint8_t n = '\x80';
    if(0x80 == n)
        cout << "now its same 0x80 again " << endl; // 输出same
    
    if(0x80 == '\x80')
        cout << "not same 0x80" << endl; // 不输出(实际不相等)
        
    if(nn == '\x80')
        cout << "not same 0x80" << endl; // 不输出(实际不相等)
        
    char nnn = '\x80';
    if(nnn == '\x80')
        cout << "same 0x80" << endl; // 输出same
        
    cout << sizeof(nn) << sizeof(n) << sizeof(lb)<< sizeof(lb)<<endl; // 输出1111
    
    
    // 高位字节>0x80但低位正常时结果正常
    hb = 0x90;
    lb = 0x70;
    universe = (hb << 8) | lb;
    cout<<universe << endl; // 输出36976
    
    line = string("\x90\x70");
    universe = (line[0] << 8) | line[1];
    cout<<universe << endl; // 输出36976
    
    return 0;
}

解决方法

将char类型转换为uint8_t后再进行位运算即可得到正确结果:

#include <string>
#include <iostream>
using namespace std;

int main() {
    string line = string("\x01\xff");
    uint8_t hb = line[0];
    uint8_t lb = line[1];

    uint16_t universe = (hb << 8) | lb; 
    cout<<universe << endl; // 输出预期的511
    return 0;
}

核心问题解答

1. 为何char与unsigned char在位运算中会被区别对待?

这是因为**char在C++中是有符号还是无符号由编译器决定**,大部分编译器默认将char视为signed char。当char的值大于等于0x80时,最高位为1,作为有符号数会被解释为负数。

在进行位运算(比如<<或|)时,C++会先执行整数提升:将小于int的类型(包括char、unsigned char、uint8_t等)提升为int类型。

  • 对于unsigned char(或uint8_t),提升为int时会进行零扩展:高位补0,比如0xff会变成0x000000ff。
  • 对于signed char(默认char),提升为int时会进行符号扩展:如果原数最高位是1(负数),高位会全部补1,比如0xff会变成0xffffffff,0x80会变成0xffffff80。

当执行(line[0] << 8) | line[1]时,line[1]作为signed char被提升为int后是全1的负数,按位或操作会把结果的所有位都置1,最终赋值给uint16_t时就变成了0xffff(即65535)。

2. 为何高位字节hb不受影响?

因为高位字节在左移8位后,原来的字节会被移动到uint16_t的高8位,而符号扩展出来的高位会被uint16_t的截断操作丢弃。

比如line[0]是0x83(作为signed char是负数),提升为int后是0xffffff83,左移8位后变成0xffff8300。当赋值给uint16_t时,只会保留低16位,即0x8300,这和预期的结果一致。而低位字节在按位或时,符号扩展出来的高位会覆盖高8位的内容,导致结果异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 18:35:01