选择索引变量的类型


11

我们大多数时候使用Integer类型表示索引变量。但是在某些情况下,我们不得不选择

std::vector<int> vec;
....

for(int i = 0; i < vec.size(); ++i)
....

这将导致编译器发出混合使用有符号/无符号变量的警告。如果将index变量设置为for( size_t i = 0; i < vec.size(); i++ ),(或unsigned int)它将解决问题。

当更加具体地使用Windows类型时,大多数Windows API都在处理DWORD(类型定义为unsigned long)。

因此,当我使用类似的迭代时,将再次引起相同的警告。现在,如果我将其重写为

DWORD dwCount;
....

for(DWORD i = 0; i < dwCount; ++i)
....

我觉得这有点奇怪。感知可能是问题。

我同意我们应该使用相同类型的索引变量,以避免索引变量可能发生范围问题。例如,如果我们使用

_int64 i64Count; // 
....

for(_int64 i = 0; i < i64Count; ++i)
....

但是对于DWORD或无符号整数,将其重写为

for(int i = 0; (size_t)i < vec.size(); ++i)

大多数人如何处理类似问题?


4
为什么要使用带符号的整数来表示索引?这就像使用整数向量存储字符串。
西蒙·托斯

3
@Let_Me_Be:因为它使边界条件更易于测试。例如,在计数降至零的情况下,执行循环主体之前的小于测试不能与无符号值一起使用。同样,最多计数不起作用。当然,在这种情况下,带符号整数也不起作用(因为它不能表示如此大的值)。
Yttrill 2012年

“这将导致编译器发出混合使用有符号/无符号变量的警告。” 那只是您必须解决的两个问题之一。在许多情况下std::size_t,其等级高于int(甚至更长)。如果向量的大小超过std::numeric_limits<int>::max(),您会后悔使用int。
Adrian McCarthy

Answers:


11

vector有一个typedef,它告诉您要使用的正确类型:

for(std::vector<int>::size_type i = 0; i < thing.size(); ++i)
{
}

虽然几乎总是将其定义为size_t,但是您不能依赖它


8
恕我直言,这实际上并没有提高可读性。
布朗

不,但是因为这是告诉向量索引使用哪种正确类型的唯一方法,所以这并不重要……
JohnB 2011年

4
这些天在c ++ 11中只使用auto
JohnB 2014年

6
@JohnB你的意思是喜欢auto i = 0吗?i变成一点都没有帮助int
天顶

1
使用可以提高可读性using index_t = std::vector<int>::size_type;
Toby Speight

4
std::vector<int> vec;

for(int i = 0; i < vec.size(); ++i)

为此使用迭代器,而不是for循环。

对于其他变量,只要变量类型的大小相同,就static_cast可以正常工作(即DWORDint16_t


2
for (std::vector<int>::iterator i = vec.begin(); i != vec.end(); ++i)很难写。拥有for (auto i = vec.begin();...更具可读性。当然,foreach在C ++ 11中也是如此。
大卫·桑利

3

您描述的情况也是我在C ++中不喜欢的事情之一。但是我学会了通过使用

for( size_t i = 0; i < vec.size(); i++ )

要么

for( int i = 0; i < (int)vec.size(); i++ )

(当然,只有在没有任何int溢出风险的情况下才使用后者)。


3

之所以警告您有关有符号和无符号之间的比较是因为有符号的值很可能会转换为无符号,这可能不是您所期望的。

在你的例子(比较intsize_t),int将被隐式转换为size_t(除非int以某种方式具有更大的范围比size_t)。因此,如果the int为负数,则由于折回,它可能会大于您要比较的值。如果您的索引永远不会为负,这将不是问题,但是您仍然会收到该警告。

相反,使用无符号的类型(例如unsigned intsize_t,或,作为约翰乙建议std::vector<int>::size_type),用于索引变量:

for(unsigned int i = 0; i < vec.size(); i++)

但是,在递减计数时要小心:

for(unsigned int i = vec.size()-1; i >= 0; i--) // don't do this!

上面的方法不起作用,因为i >= 0i未签名的情况下总是如此。相反,请使用“ 箭头运算符 ”进行递减计数的循环:

for (unsigned int i = vec.size(); i-- > 0; )
    vec[i] = ...;

正如其他答案所指出的那样,您通常希望使用迭代器遍历a vector。这是C ++ 11语法:

for (auto i = vec.begin(); i != vec.end(); ++i)

1
这仍然冒着一个风险,即unsigned int它的大小不足以容纳该大小。
Adrian McCarthy

2

C ++ 11的新选项,您可以执行以下操作

for(decltype(vec.size()) i = 0; i < vec.size(); ++i) {...}

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

尽管它的重复次数比基本的for循环要多一些,但它的耗时远不及'11之前的指定值的方式,并且一致地使用此模式对大多数(如果不是全部)可能的术语都适用要进行比较,这非常适合代码重构。它甚至适用于像这样的简单情况:

int x = 3; int final = 32; for(decltype(final) i = x; i < final; ++i)

此外,虽然您应该auto在设置i为某个智能值(例如vec.begin()decltype时使用,但在将其设置为零(例如)时,它会起作用,int因为0是简单的整数文字,因此auto会将其解析为。

老实说,我希望看到一种编译器机制,用于扩展auto循环增量器的类型确定,以查看所比较的值。


1

我使用强制类型转换为int,如中所示for (int i = 0; i < (int)v.size(); ++i)。是的,这很丑。我将其归咎于标准库的愚蠢设计,他们决定使用无符号整数来表示大小。(为了..什么?将范围扩大一位?)


1
在什么情况下,负数的集合大小有意义?在我看来,对各种集合大小使用无符号整数似乎是明智的选择。最后我检查了一下,以字符串的长度很少会返回负结果...
CVn

1
在什么情况下,对于简单测试(例如为if(v.size()-1 > 0) { ... }空容器返回true)有意义?问题是大小也经常用在算术中,尤其是。使用基于索引的容器,考虑到它们是未签名的,这会带来麻烦。基本上,将无符号类型用于除1)按位操作或2)模块化算术之外的其他操作会带来麻烦。
zvrba 2011年

2
好点子。虽然我没有真正看到您的特定示例的要点(我可能只是写if(v.size() > 1) { ... }一下,因为这样可以使意图更清晰,并且作为一个加分项,有符号/无符号问题将变得无效),但我确实看到了在某些特定情况下的情况签名可能很有用。我站得住了。
CVn

1
@Michael:我同意这是人为的例子。尽管如此,我还是经常使用嵌套循环编写算法:for(i = 0; i <v.size()-1; ++ i)for(j = i + 1; j <v.size(); ++ j) ..如果v为空,则外循环执行(size_t)-1次。因此,我要么必须在循环之前检查v.empty(),要么将v.size()转换为带符号的类型,我个人认为这都是丑陋的解决方法。我选择类型转换,因为它的LOC更少,没有if()s =>更少的错误可能性。(此外,在第二补码中,转换oveflow返回负数,因此循环根本不执行。)
zvrba 2011年

在16位系统中,将范围扩大1位非常有用(并将继续)。
Adrian McCarthy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.