国内程序员大多上过初中数学,更习惯有符号数,但是实际上无符号数也是可以用的,只是要注意一些东西。
这里的无符号数运算指unsigned int和unsigned long long的运算,unsigned char和unsigned short会自动提升为int因此不属于无符号数运算。
0、字面量
字面量的类型默认是从int开始,最小能表示这个数的类型,比如2147483647是int,2147483648是unsigned int。
如果需要强制unsigned int,可以加U后缀,不区分大小写,比如0u或0U。
如果需要强制long long,可以加LL后缀,不区分大小写,比如0LL。但不推荐写成0ll,容易和011混淆。
如果需要强制unsigned long long,可以加ULL或LLU后缀,不区分大小写,比如0ull、0uLL、0ULL、0LLu、0LLU。但不推荐写成0llu、0llU,容易和011u、011U混淆。
也可以使用uint32_t、int64_t、uint64_t类型和UINT32_C()、INT64_C()、UINT64_C()字面量修饰宏,比如UINT32_C(0)、INT64_C(0)、UINT64_C(0)。
1、无符号数没有溢出,只有回卷
回卷和溢出不是一个概念,回卷是feature,溢出是bug。
无符号数运算在结果小于0或大于最大值时会按2的n次方回卷,比如
0U - 1U == 0xFFFFFFFF
0xFFFFFFFF + 1U == 0U
有符号数转换为无符号数时也会回卷,比如
(unsigned long long)-1 == 0xFFFFFFFFFFFFFFFF
有符号数与宽度相同或更宽的无符号数运算(不包括有符号数作左移/右移的移位步长),会提升为无符号数,可能导致未预期的结果,因此尽量避免不加判断地将有符号数用于含有无符号数的表达式。特别要注意的是size_t是无符号数。
2、无符号数永远大于等于0
所以for (unsigned int i = 20; i--; i >= 0)是一个死循环。
正确的应该是for (unsigned int i = 20U; i--; i != UINT_MAX)。
或者for (unsigned int i = 20U; i--; i != 0xFFFFFFFF)。
3、无符号数只有大数减小数有现实意义
小数减大数会回卷得到一个非常大的数,没有现实意义。
无符号数只能表示(没有方向的)距离而不是(有方向的)差值。
如果要表示方向的话需要再声明一个变量保存方向。
int a = 1, b = 2;
int difference = a - b;
unsigned int a = 1U, b = 2U;
unsigned int b_above_a = b > a;
unsigned int distance = max(a, b) - min(a, b);