具有 signed 和 unsigned 之间隐式转换的代码的 C++ 安全性

c++ safeness of code with implicit conversion between signed and unsigned

提问人:francesco 提问时间:11/7/2018 更新时间:11/7/2018 访问量:493

问:

根据此处和此处讨论的有符号和无符号整数类型之间的隐式转换规则,当用 a 求和 时,有符号首先转换为 .unsigned intintintunsigned int

例如,考虑以下最小程序

#include <iostream>

int main()
{
   unsigned int n = 2;
   int x = -1;

   std::cout << n + x << std::endl;

   return 0;
}

然而,程序的输出是 1 正如预期的那样:首先转换为 ,并且 的总和导致整数溢出,给出“正确”的答案。xunsigned intn

在像前面这样的代码中,如果我确定这是正数,我可以假设 和 的总和给出了期望值吗?n + xunsigned int nint x

C++

评论

1赞 JGroven 11/7/2018
您是否在启用所有警告的情况下进行编译?
2赞 Fureeish 11/7/2018
@JGroven这将如何影响答案呢?
1赞 Fureeish 11/7/2018
好吧,可能等等都不是保证。编译器可能会警告可能出现的问题,但这并不意味着,给定一个特定的标准,比如“如果我确定 n + x 是正的”,它会,甚至它可能会失败
2赞 scohe001 11/7/2018
@JGroven“如果编译器警告你你在做什么,你可能不应该这样做。呃,这是一个“警告”是有原因的——提请你注意,以防万一。我在生产代码中遇到过很多情况,这些情况实际上很好(可能是因为声明本身就有效果,或者编译器是错误的)。WARNING: Unused variable X
2赞 Galik 11/7/2018
这将是实现定义的。该标准规定,有符号和无符号的位模式是相同的,但不能保证 2 的补码。因此,这将由实现定义,这是否有效。我相信这保证是 2 的补充,所以它们总是有效的。std::int8/16/32_t

答:

5赞 geza 11/7/2018 #1

在像前面这样的代码中,如果我确定 n + x 是正数,我可以假设无符号 int n 和 int x 的总和给出了预期值吗?

是的。

首先,使用模算术将有符号值转换为无符号

如果目标类型为无符号,则结果值是与源整数全等的最小无符号整数(模 2n,其中 n 是用于表示无符号类型的位数)。

然后使用模算术将两个无符号值相加

无符号整数应遵守算术模 2 n 定律,其中n 是该特定大小的整数的值表示中的位数。

这意味着你会得到预期的答案。

甚至,如果结果在数学意义上是负数,则C++中的结果将是一个模等于负数的数字。

请注意,我在这里假设您添加两个相同大小的整数。

1赞 Stephan Lechner 11/7/2018 #2

我认为您可以确定,并且它没有定义实现,尽管当涉及到不使用二补码表示负值的系统时,此声明需要对标准进行一些解释。

首先,让我们陈述清楚的事情:无符号积分不会溢出,而是取模 2^nrOfBits 值(参见这个在线 C++ 标准草案):

6.7.1 基本类型

(7)无符号整数应服从算术模2n定律 其中 n 是该值表示中的位数 整数的特定大小。

因此,这只是一个负值是否被正确转换为无符号整数位模式的问题,以便始终与 相同。对于使用二进制补码的系统,事情很清楚,因为二号补码实际上被设计成这种算术立即起作用。nvnv(conv)x + nv(conv)x - nv

对于使用其他负值表示的系统,我们必须仔细阅读该标准:

7.8 积分转换

(2) 如果目标类型为无符号,则结果值为 与源整数全等的最小无符号整数(模 2n 其中 n 是用于表示无符号类型的位数)。[ 注意:在二进制补码表示中,此转换为 概念性,并且位模式没有变化(如果有 notruncation)。——尾注]

正如脚注明确指出的那样,在二的补码表示中,位模式没有变化,我们可以假设在除 2s 补码以外的系统中,将发生真正的转换,使得 .x + nv(conv) == x - nv

因此,由于 7.8 (2),我会说您的假设是有效的。

评论

1赞 M.M 11/7/2018
算术是根据值而不是表示来定义的。系统是否使用 2 的补码完全无关紧要。 无论如何都必须给予。2 + -11
0赞 Stephan Lechner 11/7/2018
@M.M:是的,这就是为什么结果不依赖于实现,尽管在其他地方有人认为它可能只在 2s 补码的情况下有效。