关于在C族语言中使用有符号整数

时间:2011-12-08 07:58:31

标签: c coding-style integer

在我自己的代码中使用整数值时,我总是试着考虑签名,问自己整数应该是有符号还是无符号。

当我确定该值永远不需要为负数时,我会使用无符号整数 我不得不说这种情况大多数时间都会发生。

在阅读其他人的代码时,我很少看到无符号整数,即使代表的值不能为负数。

所以我问自己:«这是否有充分的理由,或者人们只是使用签名整数,因为不关心»

我在这里以及在其他地方搜索过这个主题,我不得不说在适用时我找不到使用无符号整数的充分理由。

我遇到了这些问题:«Default int type: Signed or Unsigned?»和«Should you always use 'int' for numbers in C, even if they are non-negative?»,它们都提供了以下示例:

for( unsigned int i = foo.Length() - 1; i >= 0; --i ) {}

对我而言,这只是糟糕的设计。当然,它可能会导致无限循环,无符号整数 但是在循环之前检查foo.Length()是否为0是如此困难吗?

所以我个人认为这不是一直使用有符号整数的好理由。

有些人也可能会说有条件的整数可能很有用,即使对于非负值,也可能会提供错误标记,通常为-1

好的,拥有一个特定值意味着“错误”是件好事 但是,对于那个特定值,UINT_MAX之类的东西有什么问题?

我实际上是在问这个问题,因为它可能会导致一些巨大的问题,通常是在使用第三方库时。

在这种情况下,您经常需要处理有符号和无符号值。

大多数情况下,人们根本不关心签名,只需将unsigned int分配给signed int,而无需检查范围。

我不得不说我有点paranoid with the compiler warning flags,所以在我的设置中,这样的隐式转换会导致编译错误。

对于那种东西,我通常使用函数或宏来检查范围,然后使用显式转换进行分配,如果需要则引发错误。

这对我来说似乎合乎逻辑。

作为最后一个例子,因为我也是Objective-C开发人员(注意这个问题与Objective-C无关):

- ( NSInteger )tableView: ( UITableView * )tableView numberOfRowsInSection: ( NSInteger )section;

对于那些不熟悉Objective-C的人,NSInteger是有符号整数 对于特定部分,此方法实际上检索表视图中的行数。

结果将从不成为负值(顺便说一下,作为节号)。

那么为什么要使用有符号整数呢? 我真的不明白。

这只是一个例子,但我总是看到那种东西,包括C,C ++或Objective-C。

所以,我只是想知道人们是否只是不关心这类问题,或者是否最终有良好有效的理由不对这种情况使用无符号整数。

期待您的回答:)

4 个答案:

答案 0 :(得分:5)

  • signed返回值可能会产生更多信息(想想错误编号,0有时是有效答案,-1表示错误,请参阅man read) ......这可能与图书馆的开发者有关。

  • 如果您担心使用unsigned代替signed时获得的额外一点,那么您可能无论如何都使用了错误的类型。 (也有点“过早优化”的说法)

  • python,ruby,jscript等语言在没有signed vs unsigned的情况下做得很好。这可能是一个指标......

答案 1 :(得分:2)

对于广泛无符号整数,有一个重量级参数:

  

过早优化是万恶之源。

我们至少有一次被无符号整数咬了。有时像在你的循环中,有时在其他环境中。无符号整数会给你的程序增加一个危险,即使它很小。而你正在引入这种危险来改变一位的含义。一个小的,微小的,微不足道的 - 但它的符号意义的一点。另一方面,我们在面包和黄油应用中使用的整数通常远低于整数范围,更多的是10 ^ 1而不是10 ^ 7。因此,在绝大多数不需要的情况下,无符号整数的不同范围。当它需要时,很可能这个额外的位不会削减它(当31太少时,32很少就足够了)并且你无论如何都需要更宽或任意宽的整数。在这些情况下,实用的方法是只使用有符号整数,并避免偶尔出现下溢错误。您作为程序员的时间可以更好地利用。

答案 2 :(得分:1)

来自C FAQ

first question in the C FAQ我们应该决定使用哪种整数类型?

  

如果您可能需要较大的值(大于32,767或小于-32,767),请使用long。否则,如果空间非常重要(即,如果有大型阵列或许多结构),请使用short。否则,使用int。如果明确定义的溢出特性很重要而负值不重要,或者如果您想在操作位或字节时避免出现符号扩展问题,请使用相应的无符号类型之一。

另一个问题涉及类型转换:

  

如果操作涉及有符号和无符号整数,则情况稍微复杂一些。如果无符号操作数较小(可能我们在unsigned int和long int上操作),那么较大的有符号类型可以表示较小的无符号类型的所有值,则无符号值将转换为较大的有符号类型,结果有更大的签名类型。否则(即,如果有符号类型不能表示无符号类型的所有值),则两个值都将转换为公共无符号类型,结果具有该无符号类型。

你可以找到它here。因此,基本上使用无符号整数,主要用于算术转换可能会使情况复杂化,因为您必须使所有整数无符号,或者冒着使编译器和您自己混淆的风险,但只要您知道自己在做什么,这本身并不存在风险。但是,它可能会引入简单的错误。

使用无符号整数是否合适?一种情况是使用按位运算:

  

<<运算符将其第一个操作数左移一些位   由第二个操作数给出,在右边填入新的0位。   同样,>> operator将其第一个操作数右移。如果   第一个操作数是无符号的,>>从左边填0位,但如果   第一个操作数已签名,>>如果高阶,可能会填充1位   比特已经是1.(这样的不确定性是它的原因之一   通常最好在使用时使用所有未签名的操作数   按位运算符。)

取自here 我在某个地方见过这个:

  

如果最好对从不为负的值使用无符号整数,我们就可以在main函数int main(int argc, char* argv[])中使用unsigned int开始。有一件事是肯定的,argc永远不会消极。

修改

正如评论中所提到的,main的签名是由于历史原因,显然它早于unsigned关键字的存在。

答案 3 :(得分:0)

无符号整数是过去的工件。这是从时间开始,处理器可以更快地执行无符号算术。

这是过早优化的情况,被认为是邪恶的。

实际上,在2005年AMD推出x86_64(或AMD64,它是如何被称为),x86的64位架构时,它们带来了过去的重影:如果有符号整数用作索引和编译器无法证明它永远不会消极,必须插入一个32到64位的符号扩展指令 - 因为默认的32到64位扩展名是 unsigned (64位寄存器的上半部分被清除如果你将32位值移入其中)。

但我建议不要在任何算术中使用 unsigned ,无论是指针算术还是简单的数字。

for( unsigned int i = foo.Length() - 1; i >= 0; --i ) {}

任何最近的编译器都会警告这样的构造, condition ist always true 或类似。使用带符号的变量可以避免这些陷阱。而是使用ptrdiff_t

问题可能是c ++库,它经常使用size_t的无符号类型,这是必需的,因为在32位上有一些非常大的大小(在2 ^ 31和2 ^ 32之间)的罕见极端情况具有某些启动开关的系统(/ 3GB窗口)。

还有更多,我认为签名和无符号之间的比较,其中签名值自动升级为无符号,因此成为一个巨大的正数,之前它是一个小的负数。

存在使用unsigned的一个例外:对于位字段,标志,掩码,这是很常见的。通常将这些变量的值解释为一个大小是没有意义的,读者可以从这个类型中推断出该变量的位数。

  

结果永远不会是负值(顺便说一下,作为节号)。那么为什么要使用有符号整数?

因为您可能希望将返回值与有符号值进行比较,这实际上是负值。在这种情况下,比较应返回 true ,但C标准指定在这种情况下签名的get被提升为unsigned,而您将获得 false 。我不知道ObjectiveC。