【问题标题】:C++ difference between unsigned int and unsigned long intC++ unsigned int 和 unsigned long int 的区别
【发布时间】:2017-09-10 05:53:54
【问题描述】:

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

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

我通过 Google 和 SO 搜索了一下,找到了这些参考资料。

  1. Types documentation cppreference.com
  2. Types documentation MSDN for Visual Studio 2015
  3. Types documentation 用于 GNU C/C++(G++ compiler 表示 C/C++ 使用相同的默认类型实现,我在这里参考 C 文档)

我假设 cppreference 文档是 ISO C++11 标准的总结。因此,从“标准”来看,unsignedunsigned int 是 16/32 位,具体取决于 LP/ILP 32/64 数据模型,而 unsigned longunsigned long int 为 32/64 位,具体取决于 LP/ILP 32/64 数据模型。

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

所以我的问题如下:

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

提前致谢。

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

那么我的问题1会变成:

  1. 对于 unsigned long 64 位,超出上限 4,294,967,295 会导致自身回绕吗?

【问题讨论】:

  • 请注意,即使 unsigned intunsigned long 恰好具有相同的范围和表示形式,它们仍然是不同的类型。

标签: c++ visual-studio gcc g++


【解决方案1】:

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

还要注意,溢出 unsigned 整数并不是未定义的行为。定义的行为是值环绕。 (例如std::numeric_limits< uint64_t >::max() + 43 == 42)。对于溢出是未定义行为的 signed 类型而言,情况并非如此。

要回答您的最后一个问题,一个 64 位整数可以存储 [0, 264 - 1] 范围内的值。 232 + 1 不会导致回绕。

【讨论】:

  • 相当肯定有符号溢出是实现定义的,而不是直接的未定义行为。也许不是 [expr] 注释 4 似乎仍然称它为 Undefined as of N4594
  • 感谢您的快速回答。只是与此相关的另一个问题。当我们在 库中拥有所有这些好的数据类型时,为什么我们仍然需要 short、int、long 及其对应的 unsigned 类型?或者从另一个角度来看,为什么 short、int、long 及其对应的 unsigned 实现不遵循与 中的类型相同的规则?
  • cstdint 中的类型是相对新的。委员会无法在不破坏大量代码的情况下删除旧类型。并且可能仍然存在出于性能原因让编译器选择整数大小更好的情况。
  • std::numeric_limits< std::uint64_t >::max() + 4241,而不是 42
  • @user4581301:不,有符号算术溢出具有未定义的行为(在 C 中,自 1989 年 ANSI 标准以来就是这种情况)。签名转换上的溢出会产生实现定义的结果。
【解决方案2】:

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

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

这些类型的范围和大小由实现定义。实施必须满足最低要求,并符合其选择的文档。

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

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

回答你的问题

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

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

如果我有一个在 Visual Studio 中编译的在 Windows 系统上运行的应用程序,基本上都是 unsigned == unsigned long。对还是错?

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

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

其实是假的。

只有在移植依赖于这些假设正确的代码时才是正确的。

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

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

严格来说并非如此。实际上,通常是正确的。

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

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

【讨论】:

  • 感谢@Peter 的详细回答。我认为unsigned short int 在某种程度上适用于 0-65535 范围内的 16 位数据类型。只是对为什么unsigned shortunsigned intunsigned longunsigned long long 之间存在重叠感到困惑。
  • 重叠是历史性的,继承自 C。short 类型(signedunsigned)是平台支持的“小”类型,用于节省内存。 long 类型是平台支持的 long(er) 类型。 int 类型是“本机”类型(例如,在硬件中实现寄存器和操作),实际上,在某些机器上相当于 short,在其他机器上相当于 long。后来,更多的硬件原生支持 32 位类型,所以intlong 等价变得越来越普遍。然后是对 64 位的硬件支持,long long 类型诞生了
  • 它比这更复杂一些(例如,有机器和它们的编译器,它们使用整数类型的奇数位)。但是,就 16 到 32 到 64 位而言,这类事情是类型的原因,以及它们之间的重叠。
猜你喜欢
  • 1970-01-01
  • 2011-12-12
  • 2012-01-27
  • 2016-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-20
相关资源
最近更新 更多