为什么int和而不是unsigned int用于C和C ++ for循环?

问题描述 投票:39回答:10

这是一个相当愚蠢的问题,但为什么在为C或C ++中的数组定义for循环时,常用int而不是unsigned int

for(int i;i<arraySize;i++){}
for(unsigned int i;i<arraySize;i++){}

我认识到在执行除数组索引之外的其他操作时使用int的好处以及使用C ++容器时迭代器的好处。是不是因为在循环数组时无关紧要?或者我应该一起避免它并使用不同的类型,如size_t

c for-loop int unsigned
10个回答
29
投票

这是一种更普遍的现象,通常人们不会使用正确的整数类型。 Modern C具有语义typedef,它比原始整数类型更优选。例如,所有“大小”都应该输入为size_t。如果系统地将语义类型用于应用程序变量,那么使用这些类型的循环变量也会变得更加容易。

而且我已经看到了几个很难发现的错误来自使用int左右。代码突然崩溃在大矩阵和类似的东西上。只需使用正确的类型正确编码即可避免这种情


0
投票

考虑以下简单示例:

int max = some_user_input; // or some_calculation_result
for(unsigned int i = 0; i < max; ++i)
    do_something;

如果max恰好是负值,比如-1,则-1将被视为UINT_MAX(当两个具有sam等级但不同符号的整数被比较时,已签名的一个将被视为无符号的一个)。另一方面,以下代码不会出现此问题:

int max = some_user_input;
for(int i = 0; i < max; ++i)
    do_something;

给出负max输入,将安全地跳过循环。


35
投票

从索引数组的逻辑角度来看,使用int更为正确。

C和C ++中的unsigned语义并不真正意味着“不是负面的”,而是更像是“bitmask”或“modulo integer”。

要理解为什么unsigned不是一个“非负”数字的好类型,请考虑

  • 将可能为负的整数添加到非负整数,您将得到一个非负整数
  • 两个非负整数的差异总是非负整数
  • 将非负整数乘以负整数会得到非负结果

显然,上述短语都没有任何意义......但它是C和C ++ unsigned语义确实有效的方式。

实际上使用unsigned类型的容器大小是C ++的设计错误,不幸的是我们现在注定要永远使用这个错误的选择(为了向后兼容)。你可能喜欢这个名字“unsigned”,因为它类似于“非负面”,但这个名字是无关紧要的,重要的是语义......而unsigned与“非负面”相差甚远。

因此,当在矢量上编码大多数循环时,我个人首选的形式是:

for (int i=0,n=v.size(); i<n; i++) {
    ...
}

(当然假设矢量的大小在迭代期间没有变化,并且我实际上需要体内的索引,否则for (auto& x : v)...更好)。

这种尽快逃离unsigned并使用普通整数的优点是避免了由于unsigned size_t设计错误导致的陷阱。例如考虑:

// draw lines connecting the dots
for (size_t i=0; i<pts.size()-1; i++) {
    drawLine(pts[i], pts[i+1]);
}

如果pts向量为空,上面的代码将会出现问题,因为在这种情况下pts.size()-1是一个巨大的无意义数字。处理a < b-1a+1 < b不同的表达式,即使对于常用值,也就像在雷区中跳舞一样。

历史上,使size_t无符号的理由是能够使用额外的比特来表示值,例如能够在16位平台上拥有数组中的65535个元素而不是32767个元素。在我看来,即使在那个时候,这个错误的语义选择的额外成本也不值得获得(如果32767元素现在还不够,那么65535就不够长了)。

无符号值非常有用,但不是用于表示容器大小或索引;对于大小和索引,常规有符号整数的工作要好得多,因为语义是你所期望的。

当您需要模运算属性或希望在位级工作时,无符号值是理想类型。


4
投票

差别不大。 int的一个好处是它正在签署。因此int i < 0是有道理的,而unsigned i < 0并不多。

如果计算了索引,那么这可能是有益的(例如,如果某些结果为负,您可能会遇到永远不会进入循环的情况)。

是的,写的更少:-)


4
投票

这纯粹是懒惰和无知。您应该始终使用正确的索引类型,除非您有进一步限制可能索引范围的信息,否则size_t是正确的类型。

当然,如果从文件中的单字节字段读取维度,那么您知道它在0-255范围内,并且int将是完全合理的索引类型。同样地,如果你循环固定次数,例如0到99,int会没问题。但是还有另一个原因是不使用int:如果你在循环体中使用i%2来区别对待偶数/奇数指数,i%2是当签署ii未签约时要贵得多......


2
投票

使用int索引数组是遗留的,但仍然被广泛采用。 int只是一种通用数字类型,并不符合平台的寻址功能。如果它恰好比它更短或更长,在尝试索引超出的超大数组时可能会遇到奇怪的结果。

在现代平台上,off_tptrdiff_tsize_t保证了更多的便携性。

这些类型的另一个优点是它们为读取代码的人提供了上下文。当您看到上述类型时,您知道代码将执行数组下标或指针运算,而不仅仅是任何计算。

因此,如果您想编写防弹,便携和上下文敏感的代码,您可以通过几次击键来完成。

GCC甚至支持typeof扩展,这使您无需在整个地方键入相同的类型名称:

typeof(arraySize) i;

for (i = 0; i < arraySize; i++) {
  ...
}

然后,如果更改arraySize的类型,i的类型会自动更改。


0
投票

我使用int因为它需要更少的物理类型而且没关系 - 它们占用相同的空间量,除非你的数组有几十亿个元素,否则如果你不使用16位编译器就不会溢出,我通常不是。


0
投票

这真的取决于编码员。有些编码员更喜欢类型完美主义,所以他们会使用他们所比较的任何类型。例如,如果他们正在迭代C字符串,您可能会看到:

size_t sz = strlen("hello");
for (size_t i = 0; i < sz; i++) {
    ...
}

如果他们只是做了10次,你可能仍会看到int

for (int i = 0; i < 10; i++) {
    ...
}

0
投票

因为除非你有一个大小超过2千兆字节类型char,或4千兆字节类型short或8千兆字节类型int等的数组,否则变量是否签名并不重要。

那么,为什么在键入较少的时候输入更多?


0
投票

除了输入更短的问题之外,原因是它允许负数。

由于我们不能提前说一个值是否可以是负数,因此大多数采用整数参数的函数都采用有符号变量。由于大多数函数使用有符号整数,因此对于像循环之类的东西使用有符号整数通常会更少。否则,你有可能不得不添加一堆类型转换。

当我们迁移到64位平台时,有符号整数的无符号范围应该足以满足大多数目的。在这些情况下,没有太多理由不使用有符号整数。

© www.soinside.com 2019 - 2024. All rights reserved.