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

Elp*_*rto 41 c int unsigned for-loop

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

for(int i;i<arraySize;i++){}
for(unsigned int i;i<arraySize;i++){}
Run Code Online (Sandbox Code Playgroud)

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

650*_*502 36

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

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

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

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

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

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

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

for (int i=0,n=v.size(); i<n; i++) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

(当然假设矢量的大小在迭代期间没有变化,而且我实际上需要体内的索引,否则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]);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  • @carlos:不会.如果`size_t`被正确定义,那**将是正确的方法.不幸的是,设计错误使`size_t`成为`unsigned`,因此这些值最终具有位掩码语义.除非你认为容器的大小是位掩码是正确的,否则使用`size_t`是错误的选择.不幸的是,标准C++库做出了一个选择,但没有人强迫我在我的代码中重复相同的错误.我的建议是尽可能地远离`size_t`并使用常规的int而不是使用逻辑,以便它也可以与`size_t`一起使用. (2认同)
  • 它不仅仅是16位平台.使用当前的`size_t`,您可以使用例如大小的`vector <char>`,例如在具有3G/1G内存分割的IA-32 Linux上使用2.1G.如果`size_t`被签名,那么如果你将矢量从<2G增加到更多那么会是什么?突然,大小将变为负面.这没有任何意义.语言不应强加这种人为限制. (2认同)

Jen*_*edt 31

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

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

  • 大小的正确类型是`size_t`,不幸的是`size_t`已经使用错误的类型本身(unsigned)定义,这是大量错误的来源.我更喜欢使用语义正确的代码类型(例如`int`),而不是使用形式正确但语义错误的类型.使用`int`s你可能会遇到非常大(非常大)值的错误......使用`unsigned`值时,疯狂行为更接近日常使用(0). (8认同)
  • @JensGustedt:语义错误不是一种意见,除非你认为`a.size() - b.size()`当'b`有一个元素而`a`没有时,它应该是大约四十亿是正确的.有人认为"无符号"对于非负数而言是一个很好的想法,你是正确的,但我的印象是他们过分重视名称而不是真正的意义.认为未签名对于计数器和索引不好的人是Bjarne Stroustrup ...请参阅http://stackoverflow.com/q/10168079/320726 (3认同)
  • @6502,正如我所说,意见差异很大。SO 不应该是讨论意见的地方,尤其是不参与讨论的人。Stroustrup 当然是很多东西的参考,但不是 C。 (2认同)
  • @6502不,我是说声称“无符号”和“非负”是不同的概念是荒谬的。他们是同一个概念。该问题并非未签名。问题是两个无符号值的减法应该有符号。`a.size()` 应该是 `size_t`,但是 `a.size() - b.size()` 应该是 `ptrdiff_t`,就像两个指针相减得到的不是一个指针,而是一个`ptrdiff_t`。毕竟,指针基本上与“size_t”相同。 (2认同)

R..*_*R.. 5

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

当然,如果维度是从文件中的单字节字段读取的,那么您就知道它的范围在 0-255 之间,并且int是一个完全合理的索引类型。同样,int如果您循环固定次数(例如 0 到 99),也可以。但是还有另一个不使用的原因int:如果您i%2在循环体中使用不同的方式处理偶数/奇数索引,i%2则成本要高得多什么时候i签名比什么时候i未签名...

  • 这并不能改变代码错误的事实。这是修复它的一种方法:`for (size_t i=10; i--&gt;0; )` (4认同)