【问题标题】:Should unsigned ints be used if not necessary?如果没有必要,是否应该使用无符号整数?
【发布时间】:2012-08-26 20:56:05
【问题描述】:

如果不需要额外的值范围,是否应该将变量声明为无符号整数?例如,在 for 循环中声明变量时,如果您知道它不会是负数,这有关系吗?一个比另一个快吗?像在 C++ 中一样声明 unsigned int 是不是很糟糕?

重申一下,是否应该这样做即使不需要额外的范围?我听说应该避免使用它们,因为它们会引起混淆(IIRC 这就是 Java 没有它们的原因)。

【问题讨论】:

  • 在没有必要的时候做anything有什么用?
  • @BoPersson:如果有人问“披萨还是汉堡包?”,他可能不会根据其中一个是否“必要”来回答“是”或“否”。这里的情况类似。 :)
  • 我更多地将其视为“如果我不饿,我应该吃东西吗?”。但我们可能会以不同的方式解读这个问题。
  • 我觉得有趣的是,这个其他问题本质上是重复的,但得出相反的结论:stackoverflow.com/questions/22587451/…

标签: c++ language-agnostic types


【解决方案1】:

当无符号整数具有负值没有意义时,您应该使用它们。这完全独立于范围问题。所以是的,你应该使用无符号整数类型即使不需要额外的范围,不,如果没有必要,你不应该使用unsigned ints(或其他任何东西),但你需要修改你对什么是必要的定义。

【讨论】:

  • @H2CO3:这就是例外。魔法值很糟糕。
  • “当无符号整数具有负值没有意义时,您应该使用它们。” 为什么?你可以很容易地使这个论点“如果它们的值大于 32767 没有意义,你应该使用有符号整数。”
  • @BenjaminLindley:是的,所以? signed char 的范围至少为 -128 到 127,long 的范围至少为 -2,147,483,647 到 2,147,483,647。这与“使用有符号整数,如果它们的值大于 32767 没有意义”有什么关系?
  • @BenjaminLindley 也许我不应该将讨论转移到其他整数类型,但我的观点是 OP 对“必要”的定义似乎仅与范围有关,而我认为应该考虑程序逻辑优先。
  • @BenjaminLindely:如果我有一个计算读取字符数的程序,这可能是负数吗?我可以有负数量的苹果吗?我通常使用unsigned 来表示数量和大小。我从来没有遇到过负尺寸的实物。例如,一棵树可以有负周长吗?我让编译器帮助识别或预防缺陷。
【解决方案2】:

使用 uints 的原因是它为编译器提供了更广泛的优化。例如,如果它知道 x 是正数,它可以用 'x' 替换 'abs(x)' 的实例。它还开辟了各种仅适用于正数的按位“强度减少”。如果您总是将一个 int 乘以/除以 2 的幂,那么编译器可能会用位移(即 x*8 == x

另一个例子可能是方程y = 16 * x + 12。如果 x 可以为负数,则需要进行乘法和加法。然而,如果 x 总是正数,那么不仅 x*16 项可以用 xy = (x<<4) | 12。

一般来说,“无符号”限定符为编译器提供了有关变量的更多信息,这反过来又允许它进行更多优化。

【讨论】:

  • 不幸的是,这很合理,但这是错误的。在大多数系统上,x * 8 和 x &lt;&lt; 3 即使对于负符号整数也是可以互换的。编译器知道,并且会使用移位。顺便说一下,我认为您的意思是二进制 OR:(x &lt;&lt; 4) | 12,也在大多数系统上对有符号整数有效。
  • 刚刚注意到 OR 问题,已修复。不过,我不太确定这种转变,除非
  • 这是关于右移的,当你想用它来除法时,需要保留符号位。 x86 程序集有 sar(算术右移)指令,其行为与 shr(右移)指令不同。左移不存在这个问题。此外,舍入会使除法问题复杂化,但乘法/左移也不存在舍入问题。
  • “原因”——好像有一个单一原因?号-1。
  • 程序员最关心的应该是正确的代码,只有在确定必要时才应该进行优化。使用无符号可能会引入意外错误。
【解决方案3】:

通常情况下,您应该使用无符号整数。

它们在溢出时的未定义行为等方面更具可预测性。
这本身就是一个很大的话题,所以我不会多说。
除非您确实需要有符号值,否则避免使用有符号整数是一个很好的理由。

此外,它们在范围检查时更容易使用 - 您不必检查负值。

典型的经验法则:

  • 如果您正在编写带有 index 作为控制变量的正向 for 循环,您几乎总是需要无符号整数。事实上,你几乎总是想要size_t。

  • 如果您正在编写一个以索引作为控制变量的反向for 循环,出于显而易见的原因,您可能应该使用有符号整数。可能ptrdiff_t 可以。

要小心的一件事是在不同大小的有符号和无符号值之间进行转换。
您可能需要仔细检查(或三重检查)以确保演员表按您预期的方式工作。

【讨论】:

  • “这本身就是一个很大的话题,所以我不会多说。” -- 请至少说一下一些事情 i> 关于它,因为我不知道你在说什么。
  • 因为定义的溢出不会导致恶魔飞出你的鼻子。 (catb.org/jargon/html/N/nasal-demons.html)
  • @BenjaminLindley:您是否需要数据本身的范围并不重要。很有可能,你没有。但重要的是您是否可能需要中间计算的范围。很有可能,你很有可能。如果他不这样做,那么问题就没有意义了(我写这篇文章时甚至没有看到那个编辑)。不过,您可能会觉得 this read 很有趣。
  • @Mehrdad:如果您可能需要更大的整数,那么您应该使用更大的整数类型,不要让整数简单地溢出。
  • @Mehrdad:我阅读了链接。使用更大的整数仍然是正确的,而让整数溢出是错误的,即使后者在 某些 情况下工作正常。
【解决方案4】:

int 是通用整数类型。如果您需要一个整数,并且int 满足您的要求(范围 [-32767,32767]),请使用它。

如果你有更专业的目的,那么你可以选择其他的。如果您需要一个数组索引,请使用size_t。如果您需要向量的索引,请使用std::vector&lt;T&gt;::size_type。如果您需要特定尺寸,请从&lt;cstdint&gt; 中挑选一些东西。如果您需要大于 64 位的内容,请查找 gmp 之类的库。

我想不出任何使用unsigned int 的好理由。至少,不是直接的(size_t 和来自&lt;cstdint&gt; 的一些特定大小的类型可能是unsigned int 的类型定义)。

【讨论】:

    【解决方案5】:

    当值不能为负时系统使用unsigned的问题不是Java没有unsigned,而是具有无符号值的表达式,尤其是与有符号值混合时,有时会给出如果您将 unsigned 视为具有移位范围的整数类型,则会产生令人困惑的结果。 Unsigned 是一种模块化类型,而不是将整数限制为正数或零。

    因此,传统观点认为,当您需要模块化类型或按位操作时,应使用unsigned。这种观点在 K&R 中是隐含的——看看 int 和 unsigned 是如何使用的——在 TC++PL(第 2 版,第 50 页)中更明确:

    unsigned 整数类型非常适合将存储视为位数组的用途。使用unsigned 而不是int 来获得更多位来表示正整数几乎从来都不是一个好主意。通过声明变量unsigned 来确保某些值是正数的尝试通常会被隐式转换规则打败。

    【讨论】:

      【解决方案6】:

      在几乎所有架构中,有符号操作和无符号操作的成本是相同的。因此,在效率方面,您不会因使用无符号签名而获得任何优势。但正如你所指出的,如果你使用 unsigned 你会有更大的范围

      【讨论】:

      • “如果你使用无符号,你会有更大的范围”——这仅适用于具有相同长度的整数。有符号长整数可以保存比无符号字节更大的值。
      • @H2CO3 当然基本类型应该相同(比较只在 int 和 int、long 和 long 之间有效,...)
      • 我想你会发现签名和未签名表单的范围(最大-最小值)完全相同。 (至少对于二进制补码形式)。
      • @MichaelAnderson 如果你的意思是总分,是的,你是对的。
      【解决方案7】:

      即使您的变量只应采用非负值 unsigned 也可能是一个问题。这是一个例子。假设要求程序员编写代码以打印所有整数对 (a,b),其中 0

      for (unsigned b = 0; b <= n; b++)
         for (unsigned a=0; a <=b-1; b++)
             cout << a << ',' << b << n ;
      

      这很容易纠正,但是用 unsigned 思考不如用 int 思考自然。

      【讨论】:

      • 我想你的意思是b-1 在第一次迭代中将是UINT_MAX,因此与预期的非常不同。但是由于其他原因,代码是错误的。为什么你会想 a &lt; b,然后写a &lt;=b-1?你通过使用毫无意义的神秘表达来引入错误;前者会工作。为什么在内部循环中增加b,而不是a? (以及为什么使用后增量......我猜是因为所有 2 位教程仍然可以,遗憾的是)
      猜你喜欢
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2023-03-08
      • 2017-10-08
      • 1970-01-01
      • 2016-10-01
      相关资源
      最近更新 更多