72

我想更好地理解为什么选择intover unsigned

就个人而言,除非有正当理由,否则我从不喜欢带符号的值。例如数组中的项目数,或字符串的长度,或内存块的大小等,所以这些东西通常不可能是负数。这样的值没有可能的意义。int当它在所有这些情况下都具有误导性时,为什么更喜欢它呢?

int我问这个是因为 Bjarne Stroustrup 和 Chandler Carruth 都给出了更喜欢unsigned 这里的建议(大约 12:30')

我可以看到使用intovershortlong-的论点int是目标机器架构的“最自然”数据宽度。

但是签过未签一直让我很恼火。在典型的现代 CPU 架构上,有符号值真的更快吗?是什么让他们变得更好?

4

13 回答 13

40

根据评论中的要求:我更喜欢int而不是unsigned因为......

  1. 它更短(我是认真的!)

  2. 它更通用,更直观(即我喜欢能够假设它1 - 2是 -1 而不是一些晦涩的巨大数字)

  3. 如果我想通过返回超出范围的值来表示错误怎么办?

当然也有相反的论点,但这些是我喜欢将整数声明为int而不是unsigned. 当然,这并不总是正确的,在其他情况下,unsigned它只是完成任务的更好工具,我只是专门回答“为什么有人更喜欢默认签名”的问题。

于 2013-09-13T21:30:20.623 回答
33

让我解释一下视频,正如专家简洁地说的那样。

安德烈亚历山德雷斯库

  • 没有简单的指导方针。
  • 在系统编程中,我们需要不同大小和符号的整数。
  • 许多转换和神秘的规则支配着算术(比如 for auto),所以我们需要小心。

钱德勒·卡鲁斯

  • 这里有一些简单的指导方针:
    1. 除非您需要二进制补码算术或位模式,否则请使用有符号整数
    2. 使用足够的最小整数。
    3. 否则,int如果您认为可以计算项目,则使用 64 位整数,如果它甚至超过您想要计算的数量。
  • 当您需要不同的类型或尺寸时,请不要担心并使用工具来告诉您。

Bjarne Stroustrup :

  • 使用int直到你有理由不使用。
  • 仅对位模式使用无符号。
  • 切勿混合签名和未签名

除了对签名规则的警惕之外,我的一句话从专家那里拿走了:

使用适当的类型,当您不知道时,使用 anint直到您知道为止。

于 2013-09-13T22:33:06.747 回答
20

几个原因:

  1. 算术运算unsigned总是产生无符号数,当减去可能合理导致负结果的整数时,这可能是一个问题——考虑减去货币数量以产生余额,或数组索引以产生元素之间的距离。如果操作数是无符号的,你会得到一个完美定义但几乎可以肯定毫无意义的结果,并且result < 0比较总是错误的(幸运的是现代编译器会警告你)。

  2. unsigned具有污染算术的讨厌特性,它与有符号整数混合。所以,如果你添加一个有符号和无符号并询问结果是否大于零,你可能会被咬,尤其是当无符号整数类型隐藏在一个后面时typedef

于 2013-09-13T21:30:57.550 回答
19

除了纯粹的社会学原因之外,没有理由更喜欢signedunsigned也就是说,有些人认为普通程序员没有足够的能力和/或注意力来编写适当的代码unsigned类型。这通常是各种“演讲者”使用的主要推理,无论这些演讲者有多受尊重。

实际上,有能力的程序员可以快速开发和/或学习一组基本的编程习惯用法和技能,使他们能够根据无符号整数类型编写适当的代码。

另请注意,在 C 和 C++ 语言的其他部分(如指针算术和迭代器算术)中始终存在有符号和无符号语义之间的基本区别(以表面上不同的形式)。这意味着在一般情况下,程序员实际上并没有选择避免处理特定于无符号语义的问题以及它带来的“问题”。即,无论您是否愿意,您都必须学会使用在其左端突然终止并在此处终止(而不是在远处某处)的范围,即使您坚决避免使用unsigned整数。

此外,您可能知道,标准库的许多部分已经unsigned非常依赖整数类型。强制使用有符号算术,而不是学习使用无符号算术,只会导致灾难性的糟糕代码。

在某些情况下首选的唯一真正原因signed是,在混合整数/浮点代码signed中,整数格式通常由 FPU 指令集直接支持,而unsigned格式根本不支持,这使得编译器生成额外的代码浮点值和unsigned值之间的转换。在这样的代码signed类型中可能会表现得更好。

但同时在纯整数代码中,unsigned类型可能比signed类型执行得更好。例如,整数除法通常需要额外的纠正代码才能满足语言规范的要求。只有在负操作数的情况下才需要进行更正,因此在没有真正使用负操作数的情况下会浪费 CPU 周期。

unsigned在我的实践中,我尽我所能坚持,signed只有在我真的需要时才使用。

于 2013-09-13T21:45:51.247 回答
9

C 和许多从它派生的语言中的整数类型有两种一般用例:表示数字,或表示抽象代数环的成员。对于那些不熟悉抽象代数的人来说,环背后的主要概念是环的两个项目相加、相减或相乘应该产生该环的另一个项目——它不应该崩溃或产生环外的值。在 32 位机器上,将 unsigned 0x12345678 添加到 unsigned 0xFFFFFFFF 不会“溢出”——它只会产生结果 0x12345677,它是为整数环定义的全等模 2^32(因为将 0x12345678 添加到 0xFFFFFFFF 的算术结果,即 0x112345677,与 0x12345677 mod 2^32) 一致。

从概念上讲,两种用途(表示数字,或表示整数环全等模 2^n 的成员)都可以由有符号和无符号类型来服务,并且对于这两种使用情况,许多操作是相同的,但存在一些差异。除其他外,不应期望将两个数字相加的尝试会产生除正确算术和之外的任何结果。虽然是否应该要求一种语言生成必要的代码以保证它不会(例如,会引发异常)是有争议的,但有人可能会争辩说,对于使用整数类型表示数字的代码,这种行为会更可取产生一个算术不正确的值,不应该禁止编译器这样做。

C 标准的实现者决定使用有符号整数类型来表示数字,使用无符号类型来表示整数全等模 2^n 的代数环的成员。相比之下,Java 使用有符号整数来表示此类环的成员(尽管它们在某些上下文中的解释不同;例如,不同大小的有符号类型之间的转换行为与无符号类型之间的行为不同)并且 Java 既没有无符号整数也没有任何在所有非异常情况下表现为数字的原始整数类型。

如果一种语言为数字和代数环数提供了有符号和无符号表示的选择,那么使用无符号数来表示始终为正的量可能是有意义的。然而,如果唯一的无符号类型表示代数环的成员,并且唯一表示数字的类型是有符号的,那么即使一个值总是正数,它也应该使用设计用于表示数字的类型来表示。

顺便说一下,(uint32_t)-1 为 0xFFFFFFFF 的原因是,将有符号值转换为无符号值等同于将无符号值添加到零,并且将整数添加到无符号值定义为将其幅度添加到/从根据代数环规则的无符号值,该规则指定如果 X=YZ,则 X 是该环的唯一成员,例如 X+Z=Y。在无符号数学中,0xFFFFFFFF 是唯一一个当添加到无符号 1 时会产生无符号零的数字。

于 2013-09-13T22:16:37.703 回答
8

现代架构的速度是一样的。问题unsigned int在于它有时会产生意想不到的行为。这可能会产生在其他情况下不会出现的错误。

通常,当您从一个值中减去 1 时,该值会变小。现在,使用signedunsigned int变量,有时减去 1 会产生一个更大的值。unsigned int和之间的主要区别在于intunsigned int产生矛盾结果的值是常用值 --- 0 --- 而带符号的数字远离正常操作是安全的。

至于为错误值返回 -1 --- 现代思维是抛出异常比测试返回值更好。

确实,如果你正确地保护你的代码,你就不会遇到这个问题,如果你在任何地方都虔诚地使用 unsigned 你会没事的(前提是你只是加法,从不减法,而且你永远不会接近 MAX_INT)。我到处使用 unsigned int 。但这需要很多纪律。对于许多程序,您可以通过使用int并花时间处理其他错误来解决问题。

于 2013-09-13T21:31:04.817 回答
8
  1. int默认使用:它与其他语言配合得更好

    • 最常见的领域用法是常规算术,而不是模算术
    • int main() {} // see an unsigned?
    • auto i = 0; // i is of type int
  2. unsigned用于模运算和位旋转(特别是移位)

    • 具有与常规算术不同的语义,请确保它是您想要的
    • 位移有符号类型很微妙(参见@ChristianRau 的评论)
    • 如果您在 32 位机器上需要 > 2Gb 向量,请升级您的操作系统/硬件
  3. 切勿混合有符号和无符号算术

    • 其规则复杂且令人惊讶(其中一个可以转换为另一个,取决于相对的类型大小)
    • 开启-Wconversion -Wsign-conversion -Wsign-promo(这里gcc比Clang好)
    • 标准库弄错了std::size_t(引自 GN13 视频)
    • 如果可以的话,使用 range-for,
    • for(auto i = 0; i < static_cast<int>(v.size()); ++i)如果你必须
  4. 除非您确实需要它们,否则不要使用短类型或大型类型

    • 当前架构的数据流很好地迎合了 32 位非指针数据(但请注意 @BenVoigt 关于较小类型的缓存效果的评论)
    • charshort节省空间但受到整体促销的影响
    • 你真的要算计一切int64_t吗?
于 2013-09-13T23:08:37.300 回答
7

回答实际问题:对于大量的事情,这并不重要。int处理第二个操作数大于第一个操作数的减法可能会更容易一些,但您仍然会得到“预期”的结果。

在 99.9% 的情况下绝对没有速度差异,因为有符号数和无符号数的唯一不同指令是:

  1. 使数字更长(用符号填充有符号或用零填充无符号) - 两者都需要同样的努力。
  2. 比较 - 一个带符号的数字,处理器必须考虑其中一个数字是否为负数。但同样,与有符号或无符号数字进行比较的速度是相同的——它只是使用不同的指令代码来表示“设置了最高位的数字小于未设置最高位的数字”(本质上)。[学究式地,几乎总是使用不同的比较结果进行操作——最常见的情况是条件跳转或分支指令——但无论哪种方式,都是一样的努力,只是输入的含义略有不同]。
  3. 乘除。显然,如果是有符号乘法,则需要对结果进行符号转换,如果设置了输入之一的最高位,则无符号不应更改结果的符号。再一次,努力(尽可能接近)是相同的。

(我认为还有一两种其他情况,但结果是一样的——签名或未签名并不重要,两者执行操作的努力是相同的)。

于 2013-09-13T21:38:43.457 回答
3

类型比int类型更接近于数学整数的行为unsigned

unsigned仅仅因为情况不需要表示负值而偏爱该类型是幼稚的。

问题是该unsigned类型具有接近于零的不连续行为。任何试图计算小的负值的操作都会产生一些大的正值。(更糟糕的是:由实现定义的。)

诸如此类的代数关系a < b意味着a - b < 0在无符号域中被破坏,即使对于像a = 3和这样的小值也是如此b = 4

如果将其设为无符号,则降序循环for (i = max - 1; i >= 0; i--)将无法终止。i

未签名的怪癖可能会导致影响代码的问题,无论该代码是否期望仅表示正数。

无符号类型的优点是某些在有符号类型的位级别上不可移植地定义的操作对于无符号类型也是如此。无符号类型缺少符号位,因此通过符号位进行移位和屏蔽不是问题。无符号类型适用于位掩码,以及以独立于平台的方式实现精确算术的代码。即使在非二进制补码机器上,无符号运算也将模拟二进制补码语义。编写多精度(bignum)库实际上需要使用无符号类型的数组来表示,而不是有符号类型。

无符号类型也适用于数字行为类似于标识符而不是算术类型的情况。例如,IPv4 地址可以用 32 位无符号类型表示。您不会将 IPv4 地址加在一起。

于 2013-09-13T22:00:40.483 回答
2

int是首选,因为它是最常用的。unsigned通常与位操作有关。每当我看到一个unsigned,我认为它是用来玩弄的。

如果需要更大的范围,请使用 64 位整数。

如果你使用索引来迭代东西,类型通常有size_type,你不应该关心它是有符号的还是无符号的。

速度不是问题。

于 2013-09-13T21:33:09.133 回答
2

我能想到的一个很好的理由是在检测到溢出的情况下。

对于数组中的项目计数、字符串长度或内存块大小等用例,您可能会溢出 unsigned int,即使查看变量也可能不会注意到差异。如果它是有符号整数,则变量将小于零并且明显错误。

当你想使用它时,你可以简单地检查变量是否为零。这样,您不必像无符号整数那样在每次算术运算后检查溢出。

于 2013-09-13T21:43:03.303 回答
2

对我来说,除了 0..+2,147,483,647 范围内包含在 32 位架构上的有符号和无符号整数集中的所有整数之外,我需要使用 -1(或更小)的概率比需要使用 +2,147,483,648(或更大)。

于 2013-09-16T19:09:38.220 回答
1

在进行简单的算术运算时会给出意想不到的结果:

unsigned int i;
i = 1 - 2;
//i is now 4294967295 on a 64bit machine

在进行简单比较时会给出意想不到的结果:

unsigned int j = 1;
std::cout << (j>-1) << std::endl;
//output 0 as false but 1 is greater than -1

这是因为在执行上述操作时,有符号整数会转换为无符号整数,并且它会溢出并变成一个非常大的数字。

于 2013-09-13T22:52:43.140 回答