C++无符号整数和无符号长整形整数之间的区别

C++ difference between unsigned int and unsigned long int

本文关键字:整数 之间 区别 无符号整数 无符号 C++      更新时间:2023-10-16

我正在使用Visual Studio Compiler在Windows上进行C++开发,特别是Visual Studio 2015 Update 3。

对于一些与 DSP 相关的工作,我使用的是无符号的 int/unsigned long数据类型。我想知道这两个内置的C/C++类型之间有什么区别。

我在谷歌和SO上搜索了一下,找到了这些参考资料。

  1. 有关 cppreference.com 的类型文档
  2. MSDN for Visual Studio 2015 上的类型文档
  3. GNU C/C++
  4. 的类型文档(因为 G++ 编译器声明 C/C++使用相同的默认类型实现,我在这里参考 C 文档)

我假设 cppreference 文档是 ISO C++11 标准的摘要。因此,从"标准"开始,无符号和无符号 int是 16/32 位,具体取决于 LP/ILP 32/64 数据模型,而无符号长整型和无符号长整型是 32/64 位,具体取决于 LP/ILP 32/64 数据模型。

对于MSDN和GNU文档,他们都声明无符号的int/unsigned long使用32位实现,并且可以保存高达4,294,967,295的值。然而,GNU文档也指出,根据您的系统,无符号长可能是64位,其中它与无符号长整整相同。

所以我的问题如下:

  1. 对于无符号长 64 位,超出上限 4,294,967,295 是未定义的行为还是正确的行为?
  2. 如果我有一个在Visual Studio中编译的Windows系统上工作的应用程序,基本上所有签名的==未签名的长。是真是假?
  3. 如果我有一个由 GNU 编译器在 Linux/Windows 上编译的应用程序,我必须确保是无符号长 == 无符号 int 还是无符号长 ==无符号长长以避免数据溢出。对或错
  4. 如果我有一个跨平台的应用程序,可以由所有这些Visual Studio/GNU/Clang/Intel编译器编译,我必须使用一堆预处理器清楚地对环境进行分类,以避免数据溢出。对或错

提前谢谢。

编辑:感谢您@PeterRuderman指出,超越无符号类型的 ceil 值并不是未定义的行为。

然后我的问题 1 将更改为:

  1. 对于无符号的长 64 位,超过上限 4,294,967,295 会导致自己环绕吗?

简短的回答是,是的,sizeof(unsigned)不能保证等于sizeof(unsigned long)但它确实在 MSVC 中。 如果您需要以独立于平台的方式确定整数的大小,请使用<cstdint>中的类型:uint32_tuint64_t。 避免在可移植代码中long,它可能会导致很多头痛。

另请注意,溢出无符号整数不是未定义的行为。 定义的行为是值环绕。 (例如std::numeric_limits< uint64_t >::max() + 43 == 42)。 对于溢出是未定义行为的已签名类型,情况并非如此。

为了回答最后一个问题,64 位整数可以存储 [0, 264- 1] 范围内的值。 232+ 1 不会造成环绕。

在C++标准中,unsigned int只能保证能够表示从065535的值。 实际上,这对应于 16 位。 实现(即编译器)可以提供更大范围的unsigned int,但不是必需的。

相比之下,unsigned long int保证能够表示04294967295范围内的值。 实际上,这对应于 32 位。同样,实现可能支持更大的范围。

这些类型的范围和大小是实现定义的。 实现必须满足最低要求,并记录它选择的文件。

实现提供 32 位的unsigned intunsigned long int的情况并不少见。 或者,一个或两个都是 64 位。 如您所注意到的,Visual Studio 2015 的文档指出,unsigned intunsigned long int都表示04294967295的值。

对于像 g++ 这样可以面向一系列系统的编译器,选择通常由目标系统决定。 因此,32 位系统的 g++ 版本可能支持与 64 位系统构建不同的unsigned long范围。

回答您的问题

对于无符号长 64 位,超出上限 4,294,967,295 是未定义的行为还是正确的行为?

它具有明确定义的行为,尽管它可能不会产生您期望的结果。 C++中的无符号整数类型使用模算术。 在04294967295的范围之外的整数值(可以支持更大范围的类型)将被转换,使其在该范围内(数学上等效于重复加减4294967296[注意最后一个数字])。 换句话说,它将"环绕"。

如果我有一个在Visual Studio中编译的Windows系统上工作的应用程序,基本上所有未签名的==未签名的长。是真是假?

假设Visual Studio 2015是真的,正如您链接到的文档所说。 对于未来的Microsoft产品来说,这可能是真的,也可能不是真的 - 这是一个实施决定。

如果我有一个由 GNU 编译器在 Linux/Windows 上编译的应用程序,我必须确保是无符号长 == 无符号 int 还是无符号长 == 无符号长长以避免数据溢出。对或错

实际上,是错误的。

仅当您移植依赖于这些假设为真的代码时,它才是正确的。

您可以使用一些技术,以便您的代码不依赖于此类假设是否成立。 例如,您可以安全地检测涉及两个unsigned值的操作何时溢出或下溢,并采取措施以生成所需的结果。 如果正确检查了所有操作,则可以避免依赖特定大小的类型。

如果我有一个跨平台的应用程序,可以由所有这些Visual Studio/GNU/Clang/Intel编译器编译,我必须使用一堆预处理器清楚地对环境进行分类,以避免数据溢出。对或错

不完全正确。 实际上,通常是真的。

如果你的代码坚持标准C++领域,有一些技术可以避免这样做(就像我上面提到的,能够避免依赖无符号类型的大小)。

如果您的代码使用或提供包装器,则在标准C++之外的函数(例如 Windows API,不在C++标准中的POSIX函数)那么它通常是必要的。 即使在这些情况下,也可以避免这样的事情。 例如,将使用 Windows API 的函数版本放在与使用 posix 的版本不同的源中。 并配置构建过程(makefile等) - 例如,如果为Windows构建,请不要在Unix版本中编译或链接。