C和C++中的无符号数和有符号数 位扩展
这里参照《深入理解计算机系统》
先引入几个函数:
#include <stdio.h>
typedef unsigned char * byte_pointer;
void show_bytes(byte_pointer start, size_t len)
{
size_t i;
for(i = 0; i < len; i++)
{
printf("%.2x ", start[len - 1 - i]);
}
printf("\n");
}
void show_int(int x)
{
show_bytes((byte_pointer)&x, sizeof(int));
}
void show_float(float x)
{
show_bytes((byte_pointer)&x, sizeof(float));
}
void show_pointer(void * x)
{
show_bytes((byte_pointer)&x, sizeof(void *));
}
这几个函数用来将数据拆成字节,然后打印出来;
无符号数和有符号数之间的算术运算操作
C语言会隐式地将有符号参数强制转换成无符号数,然后进行算术运算。对于标准的算术运算来说并无多大差异,但是对于像<和>这样的关系运算符来说,它会导致非直观的结果。我们可以引入无符号数常量和有符号数常量进行举例。
表达式 | 类型 | 求值 |
0 == 0U | 无符号 | 1 |
-1 < 0 | 有符号 | 1 |
-1 < 0U | 无符号 | 0* |
2147483647 > -2147483647-1 | 有符号 | 1 |
2147483647U >-214783647-1 | 无符号 | 0* |
2147483647 > (int)2147483648U | 有符号 | 1* |
-1 > -2 | 有符号 | 1 |
(unsigned)-1 > -2 | 无符号 | 1 |
注:带*的为非直观的情况。
扩展一个数字的位表示
有符号数按照符号扩展的形式扩展高位,无符号数按照零扩展的方式扩展高位。
即 有符号数扩展时高位全部以原来的最高位(符号位)补齐,无符号数扩展时高位全部以0补齐。
扩展发生在将小数据类型转换成大数据类型时,例如short int 转换成 long int类型。
注意扩展之前是无符号数,则扩展按照零扩展的方式,即使存储扩展结果的是有符号数,
扩展之前是有符号数的,则扩展按照符号扩展方式,即使存储扩展结果的是无符号数:
#include <iostream>
#include <cstdio>
using namespace std;
typedef unsigned char * byte_pointer;
void show_bytes(byte_pointer start, size_t len)
{
size_t i;
for(i = 0; i < len; i++)
{
printf("%.2x ", start[len - 1 - i]);
}
printf("\n");
}
int main()
{
long long data = (unsigned int)0x80000000;
show_bytes((byte_pointer)&data, sizeof(data));
unsigned long long udata = (unsigned int)0x80000000;
show_bytes((byte_pointer)&udata, sizeof(udata));
long long data2 = (int)0x80000000;
show_bytes((byte_pointer)&data2, sizeof(data2));
unsigned long long udata2 = (int)0x80000000;
show_bytes((byte_pointer)&udata2, sizeof(udata2));
return 0;
}
换句话说,扩展方式只与原数据的类型有关,与要转换成的类型无关,上例中,不管unsigned int转换成long long类型还是unsigned long long类型都按照零扩展方式,同样,无论int转换成long long还是unsigned long long都采取符号扩展的方式。
运行结果为:
00 00 00 00 80 00 00 00
00 00 00 00 80 00 00 00
ff ff ff ff 80 00 00 00
ff ff ff ff 80 00 00 00
知道了上述两个规则,下面我们分析一个小例子:
unsigned long long data = 100;
unsigned int ux = -1;
int x = -1;
data += ux; //求data的值
data = 100;
data += x;//求data的值
上述例子中data的值分别是什么,
分析如下:按照规则1,有无符号数参与的算数运算之前C语言会把有符号的参数强制转换成无符号的参数,data +=x;中x的值为0xffffffff,强制转换之前要进行位扩展,从4字节扩展成8字节,x是有符号数,且原最高位为1, 所以新的高4字节全部为1,0xff ff ff ff ff ff ff ff +100, 溢出后,结果为99,0x63;
data += ux; 中ux位无符号数,所以按照零扩展方式,扩展后还是0xff ff ff ff, 加100 结果为0x 01 00 00 00 63
第一处data 为0x 01 00 00 00 63,第二处data为 0x63
验证如下:
int main()
{
unsigned long long data = 100;
unsigned int ux = -1;
int x = -1;
data += ux; //求data的值
show_bytes((byte_pointer)&data, sizeof(data));
data = 100;
data += x;//求data的值
show_bytes((byte_pointer)&data, sizeof(data));
return 0;
}
运行结果为
00 00 00 01 00 00 00 63
00 00 00 00 00 00 00 63