为什么在 C++ 中混合 size_t 和 unsigned int 时模除法会出错

如何解决为什么在 C++ 中混合 size_t 和 unsigned int 时模除法会出错

给定一个程序

#include <iostream>
using namespace std;

int main()
{
     const size_t DoW = 7;
     const unsigned int DAYS_OF_WEEK = static_cast<unsigned int> (DoW);
     unsigned int dayOfFirstDay = 0;
     unsigned int _firstDayOfWeek = 1;
     unsigned int diff = (DAYS_OF_WEEK+ (dayOfFirstDay - _firstDayOfWeek) ) % DAYS_OF_WEEK;
     cout << "diff = ("  << DAYS_OF_WEEK << " + (" << dayOfFirstDay << " - " << _firstDayOfWeek << ")) %" << DAYS_OF_WEEK
         << " = " << diff << endl;
     return 0;
}

那个程序的输出

diff = (7 + (0 - 1)) %7 = 6

这是预期的。但是没有static_cast

修改程序
#include <iostream>
using namespace std;

int main()
{
     const size_t DAYS_OF_WEEK = 7;
     unsigned int dayOfFirstDay = 0;
     unsigned int _firstDayOfWeek = 1;
     unsigned int diff = (DAYS_OF_WEEK+ (dayOfFirstDay - _firstDayOfWeek) ) % DAYS_OF_WEEK;
     cout << "diff = ("  << DAYS_OF_WEEK << " + (" << dayOfFirstDay << " - " << _firstDayOfWeek << ")) %" << DAYS_OF_WEEK
         << " = " << diff << endl;
     return 0;
}

输出

diff = (7 + (0 - 1)) %7 = 3

这不是预期的。为什么?

(两个程序都在 Ubuntu 64 位上使用 g++ 9.3.0 编译)

解决方法

在您的平台上,size_t 似乎是 64 位,而 unsigned int 是 32 位。

没有 integral promotion 到 64 位1。这就是在表达式中混合 64 位操作数的危险。

所以 -1 的 32 位环绕在转换为 64 位时仍为 4294967295。

我们得到 7 + 4294967295(以 64 位执行)= 4294967302(无环绕)。

4294967302 % 7 = 3


1 除了 (unsigned) int 本身是 64 位的系统,目前不太可能。

,

size_t 的宽度大于 unsigned int 时,可能会发生这种结果。

unsigned intunsigned int 的减法环绕并产生 unsigned int0 - 1 结果为 -1,当 0xffffffff 为 4 字节长时,它可能变成 unsigned int

然后,将其与另一个 unsigned int 相加将产生 unsigned int,因此结果看起来像正常的减法和加法。

另一方面,与 size_t 相加将使其在 size_t 域中计算,因此不会发生截断并且值 7 + 0xffffffff 将被分割而不是 {{1} }.

这是一个在除法前检查值的示例代码:

7 - 1

这是an example of output

#include <iostream>
#include <ios>

int main()
{
     const size_t DoW = 7;
     const unsigned int DAYS_OF_WEEK = static_cast<unsigned int> (DoW);
     unsigned int dayOfFirstDay = 0;
     unsigned int _firstDayOfWeek = 1;
     size_t to_add = dayOfFirstDay - _firstDayOfWeek;
     size_t diff_uint = DAYS_OF_WEEK+ (dayOfFirstDay - _firstDayOfWeek);
     size_t diff_sizet = DoW+ (dayOfFirstDay - _firstDayOfWeek);
     std::cout << "sizeof(unsigned int) = " << sizeof(unsigned int) << '\n';
     std::cout << "sizeof(size_t) = " << sizeof(size_t) << '\n';
     std::cout << std::hex;
     std::cout << "to add     : 0x" << to_add << '\n';
     std::cout << "diff_uint  : 0x" << diff_uint << '\n';
     std::cout << "diff_sizet : 0x" << diff_sizet << '\n';
     return 0;
}
,

dayOfFirstDay - _firstDayOfWeek 是一个 unsigned int。由于 _firstDayOfWeek 大于 dayOfFirstDay,该值是下溢并环绕并成为 unsigned int 的最大值。我们将此值称为 max_uint

另一方面,DAYS_OF_WEEK 是一个 size_t,它可能是比 unsigned int 更宽的类型。这意味着 DAYS_OF_WEEK + max_uint 没有溢出。所以你最终计算 max_uint % 7。但是 max_uint % 7 等于 -1 ...

,

尽量减少混淆:

#include <stdio.h>
#include <stddef.h>

int main() {
  printf("0u - 1u = %u\n",0u - 1u);
  printf("7u + (0u - 1u) = %u\n",7u + (0u - 1u));
  printf("7zu + (0u - 1u) = %zu\n",size_t{7} + (0u - 1u));
}

我得到的输出:

0u - 1u = 4294967295
7u + (0u - 1u) = 6
7zu + (0u - 1u) = 4294967302

如您所见,0u - 1u 导致环绕。将这个巨大的数字添加到 unsigned int 会导致另一个环绕。将它添加到 size_t 并不因为整个值是可表示的。因此,在模数运算符之后您会得到不同的结果。

,

在这个声明的初始化表达式中

unsigned int diff = (DAYS_OF_WEEK+ (dayOfFirstDay - _firstDayOfWeek) ) % DAYS_OF_WEEK;

子表达式(dayOfFirstDay - _firstDayOfWeek)等于unsigned int类型的最大值。

因此在这个子表达式中,当 DAYS_OF_WEEK 的类型为 unsigned int

(DAYS_OF_WEEK+ (dayOfFirstDay - _firstDayOfWeek) )

发生溢出。

DAYS_OF_WEEK 的类型为 size_t 时,均未发生溢出。

这就是结果不同的原因。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?