C++无符号类型数据进行运算时需要注意【隐式符号转换】

2024-07-31 09:43:46 浏览数 (2)

前言

这是一个逻辑上的疏忽,一般来讲我们常用的数都是有符号位的,稍不注意就容易出现无符号计算的漏洞。

两个有符号正数相减为负数时,当他们为无符号数时,结果应当为一个很大的无符号数。

但在运算时,小于int的无符号数可能会出现隐式符号转换(转变成有符号的数进行计算,得到结果为负数)。

以下例子中我们可以很清楚的得出以上的结论。

代码语言:javascript复制
// 1
unsigned char a = 1;
unsigned char b = 2;

if (a - b < 0)  // a - b = -1 (signed int)
  a = 6;
else
  a = 8;

// 2
unsigned char a = 1;
unsigned short b = 2;

if (a - b < 0)  // a - b = -1 (signed int)
  a = 6;
else
  a = 8;

上述结果均为a=6

代码语言:javascript复制
// 3
unsigned int a = 1;
unsigned short b = 2;

if (a - b < 0)  // a - b = 0xffffffff (unsigned int)
  a = 6;
else
  a = 8;
  
// 4
unsigned int a = 1;
unsigned int b = 2;

if (a - b < 0)  // a - b = 0xffffffff (unsigned int)
  a = 6;
else
  a = 8;

上述结果均为a=8

如果预期为8,则错误代码:

代码语言:javascript复制
// Bad
unsigned short a = 1;
unsigned short b = 2;

if (a - b < 0)  // a - b = -1 (signed int)
  a = 6;
else
  a = 8;

正确代码:

代码语言:javascript复制
// Good
unsigned short a = 1;
unsigned short b = 2;

if ((unsigned int)a - (unsigned int)b < 0)  // a - b = 0xffff (unsigned short)
  a = 6;
else
  a = 8;

避免隐式符号转换

像前面代码中所写的,在判断语句中增加无符号声明 if ((unsigned int)a - (unsigned int)b < 0),此外,我们还可以这样写:

声明另一个无符号变量 c 去约束计算式子。

代码语言:javascript复制
// Good
unsigned short a = 1;
unsigned short b = 2;

unsigned short c = a - b; // a - b = 0xffff (unsigned short)

if (c < 0)  
  a = 6;
else
  a = 8;

在不声明新变量的情况下,我们可以直接使用 a 变量,其本身就是一个无符号变量。

代码语言:javascript复制
// Good
unsigned short a = 1;
unsigned short b = 2;

a = a - b; // a - b = 0xffff (unsigned short)

if (a < 0)  
  a = 6;
else
  a = 8;

规避处理

对于这种预期外的结果,我们还可以通过一些处理逻辑进行规避。

比如在环形缓冲区的使用场景中,我们使用无符号整数去计算索引距离时,可以通过总缓冲区大小来说明大的无符号结果。

在进行计算时我们先判断无符号变量大小,再作判断,即可避免产生一个大的无符号数,得到期望的结果。

代码语言:javascript复制
unsigned int buffer[256];
unsigned int head = 10;
unsigned int tail = 250;

unsigned int distance;
if (head >= tail) {
    distance = head - tail;
} else {
    distance = (256 - tail)   head;
}

总结

  • 无符号整数减法
    • 当两个无符号整数相减,结果为负数时,结果会被解释为一个很大的无符号数。
    • 例如,unsigned char a = 1; unsigned char b = 2; a - b 的结果是 0xFF(即 255),因为无符号整数不能表示负数。
  • 隐式类型转换
    • 在 C/C 中,算术运算符会将较小的无符号类型提升为 int 或者 unsigned int 来进行运算。这可能会导致一些意外的结果。

0 人点赞