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

时间:2011-09-20 16:57:41

标签: c for-loop int unsigned

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

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

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

10 个答案:

答案 0 :(得分:35)

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

C和C ++中的

unsigned语义并不真正意味着&#34;不是负面的&#34;但它更像是&#34; bitmask&#34;或&#34;模整数&#34;。

要理解为什么unsigned不适合&#34;非负面&#34;请考虑数字

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

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

实际上对容器的大小使用unsigned类型是C ++的一个设计错误,不幸的是我们现在注定要永远使用这个错误的选择(为了向后兼容)。你可能喜欢这个名字&#34; unsigned&#34;因为它类似于&#34;非否定&#34;但这个名字是无关紧要的,重要的是语义...... unsigned离非&#34;非否定&#34;。

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

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韩元也不够长)。

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

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

答案 1 :(得分:29)

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

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

答案 2 :(得分:4)

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

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

是的,写的更少: - )

答案 3 :(得分:4)

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

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

答案 4 :(得分:2)

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

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

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

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

GCC甚至支持typeof扩展程序,可以让您无法在整个地方输入相同的类型名称:

typeof(arraySize) i;

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

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

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

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

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

答案 7 :(得分:0)

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

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

答案 8 :(得分:0)

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

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

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

答案 9 :(得分: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输入,将安全地跳过循环。