编程C 系统程序员

解释在 C 中有符号和无符号数之间的类型转换的特点,可能出现的潜在问题,以及在有符号/无符号类型的算术和比较中如何避免意外情况,这对程序的可移植性有何影响?

用 Hintsage AI 助手通过面试

答案

在 C 中,自动类型转换遵循 "通常算术转换" 的原则。当表达式中有不同符号(有符号/无符号)的数字参与时,转换的规则如下:

  • 如果一个操作数是无符号的,另一个是有符号的 — 有符号的会自动转换为无符号的。
  • 这可能导致意外的溢出,尤其是在比较或算术操作时。
  • 类型的大小也会影响:如果无符号类型的位数更大,则有符号类型会转换为无符号类型。

危险的算术示例:

int a = -1; // 有符号 unsigned int b = 1; printf("%d ", a < b); // 始终为 false,因为 a 转换为一个非常大的无符号数

结果:-1 在转换为无符号数后,会变成一个非常大的正数。

重要需要记住的事项:

  • 如果可能引起符号混淆,始终显式进行类型转换。
  • 关注类型的大小(int,long,uint32_t 等),确保转换是可预测的。
  • 在涉及有符号和无符号变量的边界检查和算术中要分开处理。

有陷阱的问题

问题: 表达式 (int)(unsigned)-1 会返回什么结果?

预期错误答案: "-1,因为 -1 会转换为 int。"

正确答案: 在表达式 (unsigned)-1 中,首先将 -1 转换为无符号数(在32位平台上是 0xFFFFFFFF),然后再转换回有符号 int,这也依赖于实现,但通常会再次得到 -1(如果使用二进制补码)。然而,更准确地说:结果依赖于有符号数的表示标准,但在大多数实现中将会得到 -1

示例:

int x = (int)(unsigned)-1; // x == -1 在大多数平台上

由于对该主题细微差别缺乏了解而导致的实际错误示例


历史

在字符串处理程序中使用了大小比较函数:如果字符串的长度可能为负,那么程序就会报告错误。然而,长度是 size_t(无符号类型),比较代码 if(length < 0) 始终为 false,导致无限循环和内存溢出。


历史

在解析协议时,网络包中字段为无符号类型,而局部变量为有符号类型。由于处理某些值时无符号溢出,导致包长度计算不正确,这导致了缓冲区溢出的漏洞。


历史

日志中的日期比较模块将日期存储为无符号 int,寻求的日期范围为 int。一些边界值未能如预期那样引发异常,导致记录过滤不正确并丢失重要日志。