简单的一句话总结:内存中固定占用2个字节是"物理存在",运算时提升为4字节(int)是"为了干活方便"。
为了让你彻底厘清,我们把这两个概念拆开看,再连起来理解:
1. 内存中的分配(2 Byte):这是"仓库"大小
当你定义 unsigned short a = 0xFFFF; 时,编译器在内存(RAM)中划出一块固定的空间:
-
大小:2个字节(即16位)。
-
存储内容 :存储的是纯二进制数据
11111111 11111111。 -
特性 :这块内存是静止的。无论你是把这个变量加100,还是把它赋值给别的变量,只要不重新赋值,这2个字节里的数据永远不变。
打个比方 :这就像你家里有一个小储物箱 ,尺寸固定为
长2cm × 宽2cm。里面永远只能放得下 0~65535 编号的标签。
2. 运算时的提升(转为 int):这是"工作台"大小
当你写代码 a + 1 时,CPU不能直接在内存的"储物箱"里做加法,必须把数据搬到CPU内部的"寄存器(工作台)"里才能计算。
-
搬运过程 :CPU的寄存器(工作台)宽度通常为32位或64位。当CPU把16位的
0xFFFF搬到32位寄存器时,为了保持数值不变,会在前面补上16个0,变成0x0000FFFF(依然是65535)。 -
为什么要补0(提升) :如果CPU用16位工作台计算
0xFFFF + 1,结果会变成0x0000(溢出)。为了避免这种溢出,C标准规定先把数据扩展到寄存器原生宽度(32位)再算,这样0x0000FFFF + 1 = 0x00010000(65536),中间结果不会丢失精度。
打个比方 :你从"小储物箱"里取出标签,放到一个巨大的 "大办公桌"(int工作台)上展开。桌子很大(32位),标签放在桌上只占了中间一小块地方,周围全是空的。所有的加减乘除都在这个"大办公桌"上操作。
3. 把这两个概念连起来看(最关键)
很多人困惑的点在于:"既然是2个字节,为什么提到int(4个字节)?"
流程是这样的:
-
读 (Load):CPU从内存的2字节仓库 中读出
0xFFFF。 -
扩展 (Promotion):CPU把它展开成4字节 的
0x0000FFFF,放入寄存器。 -
计算(Execute):用这个4字节的数据去加、减、比较。
-
写 (Store):如果计算结果要存回
unsigned short变量,CPU会把4字节结果截断 (砍掉高2字节),只把低2字节放回那个2字节的仓库里。
4. 为什么MISRA要求加 u 后缀(结合内存理解)
既然 unsigned short(无符号)提升为 int(有符号),那这个"大办公桌"的性质就变了。
-
在32位平台 :
0xFFFF变成0x0000FFFF,最高位是0,视为正数,没问题。 -
在16位平台 (嵌入式单片机):
int的工作台也是16位。unsigned short(16位)搬上工作台,恰好占满。但如果此时值是0x8000(32768),放入16位int工作台时,最高位是1,这个值瞬间变成了负数(-32768)!
所以,MISRA要求你写 0xFFu:
此时 0xFFu 的类型是 unsigned int。当它和 unsigned short 比较时,两边都是"无符号"属性。哪怕在16位机器上,它们都会被当作无符号数来处理,绝不会因为最高位是1而被误解成负数