这是一个相当愚蠢的问题但在为C或C ++中的数组定义for循环时,为什么int
常用而不是unsigned int
?
for(int i;i<arraySize;i++){}
for(unsigned int i;i<arraySize;i++){}
我认识到在执行除数组索引之外的操作时使用int
的好处以及在使用C ++容器时使用迭代器的好处。是不是因为在循环数组时无关紧要?或者我应该一起避免使用它们,并使用其他类型,例如size_t
?
答案 0 :(得分:35)
从索引数组的逻辑角度来看,使用int
更为正确。
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-1
与a+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_t
,ptrdiff_t
和size_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
输入,将安全地跳过循环。