测试代码
void ChkTest(void)
{
unsigned int cnt1 = 1;
unsigned int cnt2 = 2;
unsigned short a = 1;
unsigned short b=-1;//等价于unsigned short b=0xFFFF;
unsigned short c=0;
c = a-b;
printk("cnt1-cnt2=%x %d\r\n",cnt1-cnt2,cnt1-cnt2);
printk("a-b = 0x%X a-b = %d c=0x%x\n",a-b,a-b,c);
}
执行结果:cnt1-cnt2=ffffffff -1
a-b = 0xFFFF0002 a-b = -65534 c=0x2
分析首先CPU没有减法操作,需要把减法转为加法运算,
则1-2转为加法操作1+(-2)对应实际正数的补码为自己,负数的补码为其反码+1,
[+1] = [00000000 00000000 00000000 00000001]原 = [00000000 00000000 00000000 00000001]反 = [00000000 00000000 00000000 00000001]补
[-2] = [10000000 00000000 00000000 0000010]原 = [11111111 11111111 11111111 11111101]反 = [11111111 11111111 11111111 11111110]补
1-2= [00000000 00000000 00000000 00000001]+[11111111 11111111 11111111 11111110]=[11111111 11111111 11111111 11111111]
这个结果使用%x输出为实际内存中的数据,
%d输出为转为有符号的结果([11111111 11111111 11111111 11111111]再求补码[10000000 00000000 00000000 00000001 ]);
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
同理a-b=1-(-1)=1+(-(-1))操作转补码:
[+1] = [00000000 00000001]原 = [00000000 00000001]反 = [00000000 00000001]补
[-(-1)]=-0xFFFF, printk("a-b = 0x%X a-b = %d c=0x%x\n",a-b,a-b,c);这句打印语句隐含了%X和%d是四字节返回值,c是2字节值;
则前段计算红色a-b值需要转为4字节运算,而c = a-b;执行2字节的运算(存在截断情况)
扩展四字节[-(-1)] = [-(11111111 11111111) ] =
[10000000 00000000 11111111 11111111) ]原 =[11111111 11111111 00000000 00000000]反 = [11111111 11111111 00000000 00000001]补
1+(-(-1))=[00000000 00000001]+[11111111 11111111 00000000 00000001]=[11111111 11111111 00000000 00000010]
这个结果使用%x输出为实际内存中的数据(0xFFFF0002),
%d输出转为有符号的结果([11111111 11111111 00000000 00000010]再求补码[10000000 00000000 11111111 11111110 ]);
c=0x%x两字节[-(-1)] = [-(11111111 11111111) ] 符号位和数据位重叠,假设扩展为4字节表示,具体值同上,符号位在最高位(这里暂未确认编译器如何处理);
1+(-(-1))=[00000000 00000001]+[11111111 11111111 00000000 00000001]=[11111111 11111111 00000000 00000010],截断后两个字节赋值给c。
这个结果使用%x输出为实际内存中的数据0x0002([00000000 00000010]补码同原码);
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
总结:
1.分析减法需要明确补码的使用方式,不能按照数学常识计算1-2=-1。
2.无符号数和有符号数进行算数运算,先转有符号为无符号,然后运算(可能存在截断情况)。
3.负数实际在内存中是以补码形式存在,%d格式输出的不是存储的原始数据,%x输出的才是内存原始数据。