我一直在讨论与同事一起使用size_t的问题。出现的一个问题是循环,它将循环变量递减直到达到零。
请考虑以下代码:
for (size_t i = n-1; i >= 0; --i) { ... }
由于无符号整数环绕,这会导致无限循环。在这种情况下你做什么?编写上面的代码并没有意识到你犯了一个错误似乎很容易。
我们团队的两个建议是使用以下样式之一:
for (size_t i = n-1; i != -1 ; --i) { ... }
for (size_t i = n; i-- > 0 ; ) { ... }
但我确实想知道还有其他选择......
答案 0 :(得分:81)
就我个人而言:
for (size_t i = n; i --> 0 ;)
它有a)没有搞笑-1
,b)条件检查是助记符,c)它以合适的笑脸结束。
答案 1 :(得分:53)
保证无符号整数很好地包裹。他们只是实现了算术模2 N 。所以这个易于阅读的习语是这样的:
for (size_t i = n-1; i < n ; --i) { ... }
这会将变量设置为您想要的初始值,显示迭代的意义(向下)并精确地给出您要处理的值的条件。
答案 2 :(得分:8)
i-1
代替i
。答案 3 :(得分:5)
您使用的是标准库容器吗?如果是这样,我喜欢reverse_iterator
vector<int> ivect;
// push, push, push...
vector<int>::reverse_iterator riter;
for(riter=riter.rbegin(); riter!=ivect.rend(); ++riter)
{
//...
}
对于原始数组,您可以使用std::reverse_iterator
键来指示是迭代器:
int i[] = {1, 2, 3, 4};
typedef std::reverse_iterator<const int*> irevit;
irevit iter(i+4);
irevit end(i);
for(; iter != end; ++iter) {
cout << *iter;
}
// Prints 4321
非连续对象迭代可以通过将对象指针存储在容器或数组中来完成:
struct Foo {
Foo(int i) I(i) { }
int I;
}
vector<Foo*> foos;
for(int i = 0; i < 10; ++i)
foos.push_back(new Foo(i));
typedef vector<Foo*>::const_reverse_iterator frevit;
frevit iter(foos.rbegin());
for(; iter != foos.rend(); ++iter) {
cout << (*iter)->I;
}
// Prints 9876543210
如果你真的想要使用裸size_t
那么为什么在其他答案中使用所有这些隐含的混乱-1欺骗? size_t
的最大值明确可用作终止值:
int is[] = {1, 2, 3, 4};
int n = 3;
for (size_t i = n; i != std::numeric_limits<size_t>::max(); --i) {
cout << is[i] << endl;
}
// prints 4321
答案 4 :(得分:5)
如果你担心意外写这样的循环,一些编译器会警告这些事情。例如,gcc通过-Wtype-limits
选项启用了警告(也由-Wextra
启用):
x.c:42: warning: comparison of unsigned expression >= 0 is always true
答案 5 :(得分:3)
i != -1
依赖于-1
默默地投射到size_t
,这对我来说似乎很脆弱,因此,在您提出的替代方案中,我肯定会选择递减一个。另一种可能性(特别是如果你在循环体中实际上不需要i
但只需要以相反的顺序迭代一个数组)就是将数组包装在std::
类容器中并且使用rbegin
和rend
方法在包装器上使用迭代器。例如,Boost.Array将支持后者的选择。
答案 6 :(得分:1)
以下是关于此主题的good discussion指针。
我会尝试:
for( size_t i = n; i != 0; i-- ) {
// do stuff with array[ i - 1 ]
}
答案 7 :(得分:1)
size_t i = n-1;
do {
...
} while ( i-- != 0);
如果需要,您可以使用if (n > 0)
将其换行。
答案 8 :(得分:1)
另一种方式(没有签名/未签名的比较):
for (size_t i = n-1; i + 1 > 0; i--)
因为
(i + 1 > 0) === (i > -1)
答案 9 :(得分:1)
在C ++ 20中,您可以使用范围和视图,如下所示:
namespace sv = std::views;
for (unsigned i : sv::iota(0u, n) | sv::reverse)
std::cout << i << "\n";
这里是demo。
该代码可读性强,并且完全避免了无符号环绕行为的任何问题,因为i
仅具有[0,n)
范围内的值。
答案 10 :(得分:0)
我发现另一种简单有效的解决方案(适用于POSIX的系统)是用ssize_t替换size_t:
for (ssize_t i = n-1; i >= 0; --i) { ... }
在非POSIX系统上,ssize_t的类型定义并不难: Alternative to ssize_t on POSIX-unconformant systems