【问题标题】:Advice on unsigned int (Gangnam Style edition)关于 unsigned int 的建议(江南 Style 版)
【发布时间】:2015-02-01 05:14:33
【问题描述】:

“江南style”视频(我相信你一定听说过)在 youtube 上的浏览​​量刚刚超过 20 亿。事实上,谷歌表示他们从没想过视频会大于 32 位整数……这暗示了谷歌使用 int 而不是unsigned 作为他们的观看计数器。我认为他们不得不重新编写代码以适应更大的视图。

查看他们的风格指南:https://google-styleguide.googlecode.com/svn/trunk/cppguide.html#Integer_Types

...他们建议“不要使用无符号整数类型”,并给出一个很好的理由:unsigned 可能有问题。

这是一个很好的理由,但可以提防。我的问题是:使用unsigned int 通常是不好的编码习惯吗?

【问题讨论】:

  • 多么愚蠢的规则:“不要使用无符号类型”。我可以想到一百万个无符号类型的用例。
  • 我完全不同意该准则。他们的例子的问题在于,当i 是unsigned int 时,他们使用i >= 0 作为条件——这太愚蠢了。当然,条件总是成立的。事实上,在这种情况下,unsigned int 已经完成了它的工作!它永远不会消极!
  • 我不同意该规则,也许是因为担心在我的answer here 中混合有符号和无符号类型时可能会发生极端情况,但老实说使用正确的警告标志和静态分析应该能发现这些问题。
  • @JosephMansfield 我认为他们意识到这很愚蠢,但这是可以而且已经发生的事情。我知道它是怎么做到的。
  • OTOH 无符号类型不太可能格式化您的硬盘。

标签: c++ styles int 32-bit unsigned


【解决方案1】:

Google 声明:“有些人,包括一些教科书作者,建议使用 unsigned 类型来表示从不为负的数字。这是一种自我记录的形式。”

我个人使用unsigned ints 作为索引参数。

int foo(unsigned int index, int* myArray){
    return myArray[index];
}

Google 建议:“使用断言记录变量为非负数。不要使用无符号类型。”

int foo(int index, int* myArray){
    assert(index >= 0);
    return myArray[index];
}

Google 专家:如果在调试模式下传递了一个负数,我的代码有望返回一个越界错误。 Google 的代码保证断言。

对我来说很专业:我的代码可以支持更大的myArray。

我认为实际的决定因素归结为,您的代码有多干净?如果您清除 所有 警告,那么当编译器警告您知道您何时尝试将有符号变量分配给无符号变量时,就会很清楚。如果你的代码已经有一堆警告,那么编译器的警告就会消失在你身上。

最后一点:谷歌说:“有时 gcc 会注意到这个错误并警告你,但通常不会。”我还没有看到在 Visual Studio 上会出现这种情况,检查负数和从有符号到无符号的赋值总是会被​​警告。但如果你使用 gcc,你可能会很小心。

【讨论】:

  • 您的 pro 实际上是要求 size_t 为无符号类型的最初动机。它在 16 位机器上有效(当时很普遍),但在今天真的不是很重要。问题是 C++(和 C)无符号类型不能很好地模拟整数,甚至非负整数集,应该避免这种情况。
  • 例如,减法没有在非负整数集上定义。非负整数集是整数集的子集,减法返回可能为负的结果,或者您不支持减法。如果abs(i1 - i2)是合法的,例如,它必须导致两个值之间的差异。
  • std::abs(i1 - i2) 甚至不会编译假设您将 <cstdlib> 包含在整数版本中,而不是 <cmath> 用于浮点版本。我在 gcc 中得到了 error: call of overloaded ‘abs(unsigned int)’ is ambiguous。
  • @JamesKanze 减法在以某个非负整数为模的有限非负整数集上得到了很好的定义。模算术的整个研究领域都依赖于它的定义。事实上,我想说,对模算术中减法行为非常熟悉的人比二进制补码算术要多得多。
  • @JamesKanze “它被定义为一种对大多数用途来说都是错误的方式。”我知道你只是想在这里说明一点,但“大多数用途”真的吗?
【解决方案2】:

Google 规则在专业圈子中被广泛接受。问题 是无符号整数类型有点损坏,并且有 用于数值时的意外和不自然行为;他们 不能作为红衣主教类型。例如,数组的索引 可能永远不会是负面的,但写起来很有意义 abs(i1 - i2) 查找两个索引之间的距离。如果 i1 和 i2 具有无符号类型。

作为一般规则,Google 风格指南中的这一特定规则 或多或少符合语言设计者的意图。 任何时候你看到除了int 以外的东西,你可以假设一个特殊的 原因。如果是因为范围,它将是long 或 long long,甚至是int_least64_t。使用无符号类型通常是 您正在处理位的信号,而不是 变量,或者(至少在 unsigned char 的情况下)你是 处理原始内存。

关于使用unsigned 的“自我记录”:this 不成立,因为几乎总是有很多价值观 变量不能(或不应该)取,包括许多积极的变量。 C++ 没有子范围类型,unsigned 的定义方式意味着 它也不能真正用作一个。

【讨论】:

  • 我认为在这种情况下“广泛”是非常主观的。或许我的职业生涯很幸运。就我个人而言,我发现无符号数及其模算术在数学上比有符号数更有意义。顺便说一句,我认为 abs(i1-i2) 没有意义,但我喜欢 clang 的警告。
  • 你的例子有缺陷。 std::abs(i1 - i2) 甚至不会编译假设您将 <cstdlib> 包含在整数版本中,而不是在 <cmath> 中包含浮点版本。我在 gcc 中得到了 error: call of overloaded ‘abs(unsigned int)’ is ambiguous。
  • 请注意,这不是警告,即使没有启用警告,它也是一个错误。
  • 为什么要推测 C++ 的设计者对unsigned 的意图是什么? Stroustrup 合作制定了JTSF with Lockheed Martin 的指导方针。在这些指南中,他特别提到了unsigned 的必要用法,并禁止对它们进行算术运算。
  • @TravisBemrose 当整数(在这种情况下是无符号的)太大而无法表示为(无符号)整数时,因此开始“换行”(发生所谓的“溢出”) .在这种情况下,旧值 (x) 实际上大于旧值 (x + 1)。见en.wikipedia.org/wiki/Integer_overflow。
【解决方案3】:

本指南极度具有误导性。盲目地使用int 而不是unsigned int 不会解决任何问题。这只是将问题转移到其他地方。在对固定精度整数进行算术运算时,您绝对必须注意整数溢出。如果您的代码的编写方式不能很好地处理某些给定输入的整数溢出,那么无论您使用signed 还是unsigned ints,您的代码都会被破坏。对于unsigned ints,您还必须注意整数下溢,对于doubles 和floats,您必须注意浮点运算的许多其他问题。

请以 Google 发布的this article about a bug in the standard Java binary search algorithm 为例,了解为什么您必须注意整数溢出。事实上,那篇文章展示了 C++ 代码转换为 unsigned int 以保证正确的行为。这篇文章还从介绍 Java 中的一个错误开始,你猜怎么着,他们没有unsigned int。但是,他们仍然遇到了整数溢出的错误。

【讨论】:

  • 你必须知道任何值的有效范围,不管它是否有符号。这里真正的问题是一些作者似乎建议使用unsigned,就好像它是一个子范围类型一样。它不是; C++ 没有子范围类型。
  • 我认为上溢/下溢不是问题。我认为问题在于尝试使用无符号进行算术运算时(经常被忽视的)陷阱。
【解决方案4】:

为您将执行的操作使用正确的类型。 float 对计数器没有意义。 signed int 也没有。计数器上的正常操作是print 和+=1。

即使您有一些不寻常的操作,例如打印观看次数的差异,您也不一定会遇到问题。当然,其他答案提到了不正确的abs(i2-i1),但期望程序员使用正确的max(i2,i1) - min(i2,i1) 并不是没有道理的。 signed int 确实存在范围问题。这里没有统一的解决方案;程序员应该了解他们正在使用的类型的属性。

【讨论】:

    【解决方案5】:

    您的具体问题是:

    “使用无符号是不好的做法吗?”唯一正确的答案可能是否定的。这不是坏习惯。

    有许多样式指南,每一个都有不同的重点,虽然在某些情况下,一个组织,鉴于其典型的工具链和部署平台可能会选择不为其产品使用 unsigned,但其他工具链和平台几乎要求使用它。

    Google 似乎受到了很大的尊重,因为他们拥有良好的商业模式(并且可能像其他人一样雇佣一些聪明人)。

    CERT IIRC 建议对缓冲区索引使用无符号,因为如果发生溢出,至少您仍会在自己的缓冲区中,那里有一些内在的安全性。

    语言和标准库设计者怎么说(可能是公认智慧的最佳代表)。 strlen 返回一个 size_t,它可能是无符号的(取决于平台),其他答案表明这是不合时宜的,因为闪亮的新计算机具有广泛的体系结构,但这忽略了 C 和 C++ 是通用编程语言并且应该在大小上很好地扩展这一点平台。

    底线是,这是许多宗教问题之一;当然还没有解决,在这些情况下,我通常会遵循我的信仰来进行绿色领域的开发,并遵循现有的代码库约定来进行现有的工作。一致性很重要。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-10
      • 2010-11-25
      • 2010-12-24
      相关资源
      最近更新 更多