使用无符号索引执行反向“for”循环的最佳方法是啥?

Posted

技术标签:

【中文标题】使用无符号索引执行反向“for”循环的最佳方法是啥?【英文标题】:What's the best way to do a reverse 'for' loop with an unsigned index?使用无符号索引执行反向“for”循环的最佳方法是什么? 【发布时间】:2010-10-14 12:35:07 【问题描述】:

我第一次尝试 reverse for loop 做了 n 次,类似于:

for ( unsigned int i = n-1; i >= 0; i-- ) 
    ...     

失败,因为在无符号算术i 保证始终大于或等于零,因此循环条件将始终为真。幸运的是,在我不得不怀疑为什么循环无限执行之前,gcc 编译器警告了我“毫无意义的比较”。


我正在寻找一种优雅的方式来解决这个问题,请记住:

    它应该是一个向后的 for 循环。 循环索引应该是无符号的。 n 是无符号常量。 不应基于无符号整数的“晦涩”环算术。

有什么想法吗?谢谢:)

【问题讨论】:

if n > int 表示的最大正值。 然后使用long。如果你的数组太长了,你会遇到比 unsigned 更严重的问题 :-) 语义,也许吧?因为我永远不应该低于零。 谁提到过数组? @Pax:例如,这可能是 16 位处理器的代码,其中 i 开始于 32767 以上。使用比 unsigned int 更大的任何东西都是低效的。 【参考方案1】:
for ( unsigned int i = n; i != 0; i-- ) 
    // do something with i - 1
    ...     

请注意,如果您同时使用 C++ 和 C,当您切换到使用迭代器时,使用 != 是一个好习惯,而

【讨论】:

另外,当循环中的任何代码进一步减少 i 时,您很快就会注意到无限循环。 (而且更容易推理)【参考方案2】:
for ( unsigned int i = n; i > 0; i-- ) 
    ...  
    i-1 //wherever you've been using i   

【讨论】:

我个人发现,如果没有大的警告注释,使用“i-1”而不是“i”是不明显的,并且当将来有人本能地使用“i”时可能会导致奇怪的错误而不是“i-1”。 是的,就像使用 0 访问数组的第一个单元格一样。生活并不容易;-) 您可以通过调用 i 'counter' 并声明一个名为 'index = counter-1' 的变量来解决这个问题。【参考方案3】:
for ( unsigned int loopIndex = n; loopIndex > 0; --loopIndex ) 
    unsigned int i = loopIndex - 1;
    ...
 

for ( unsigned int loopIndex = 0; loopIndex < n; ++loopIndex ) 
    unsigned int i = n - loopIndex - 1;
    ...
 

【讨论】:

哎呀!我在尝试删除自己的评论时不小心删除了评论。从我的缓存中得到:“长变量名使代码可读性降低的经典示例,恕我直言。” -- 抱歉,找不到撤消操作【参考方案4】:
for ( unsigned int i = n; i > 0; i-- ) 
    ...     

应该可以正常工作。如果您需要使用i 变量作为数组的索引,请执行以下操作:

array[i-1];

【讨论】:

【参考方案5】:
for ( unsigned int i = n; i > 0; i-- ) 
    unsigned int x = i - 1;
    // do whatever you want with x    

当然不优雅,但它有效。

【讨论】:

【参考方案6】:
for ( unsigned int i = n-1; (n-i) >= 0; i-- ) 
    // n-i will be negative when the loop should stop.
    ...     

【讨论】:

如果 n 和 i 都是无符号的,那么 n-i 也将是无符号的,不是吗?【参考方案7】:

怎么样:

for (unsigned i = n ; i-- > 0 ; )

  // do stuff with i

【讨论】:

@Auron,n 通常是数组的长度,所以在大多数情况下你不希望 i == n。 就我而言,这是反向循环的标准习语,我很惊讶人们以前没有遇到过。 您甚至可以将其写为i --&gt; 0,这将直观地说明意图;-) @HeathHunnicutt - 无符号整数算术具有明确定义的“模”语义。超出范围的结果将自动换行。 @HeathHunnicutt 无符号整数在 C 中永远不会溢出或下溢。仅对于有符号整数,下溢和溢出具有未定义的行为。来自标准:unsigned arithmetic does not overflow because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type..【参考方案8】:

嗯。以下是您的选择:

    使用i=0 作为您的中断条件 - 当 i 达到 0 时循环不会执行,因此在循环退出后对i=0 执行循环内容的 1 次迭代。
for ( unsigned int i = n-1; i > 0; i-- ) 
    doStuff(i);

doStuff(0);
    在循环中,测试i=0break out。不推荐,因为现在您要在循环中两次测试 i 的值。在循环中使用 break 通常也被视为不好的做法。
for ( unsigned int i = n-1; i >= 0; i-- ) 
    doStuff(i);
    if (i=0) break;

【讨论】:

我认为仅仅因为使用 unsigned int 就重复 doStuff 有点……开销?就像测试实际上根本不特殊的事物的一种特殊情况一样。【参考方案9】:

我倾向于使用

 for ( unsigned int i = n; i > 0; )  
    --i;
    ...     
 

这与skizz 的答案几乎相同,(它错过了最终不必要的递减,但编译器应该优化掉它),并且实际上将通过代码审查。我必须使用的每个编码标准在条件规则中都没有变化。

【讨论】:

现在 that 是一个残缺不全的 for 构造,很快就会被 while 语句替换! -1 。 xtofl -- 不,不是 while 语句。 Skizz 展示了最好的方法。 while 语句需要在其范围之外,在独立语句中声明其循环变量。 不幸的是,Skizz 的回答在大多数商店都无法通过代码审查。 “条件中的无突变” - 我以为我们是程序员,而不是(代码)猴子!无论代码因此受到影响,都应该打破规则。使用“i-1”索引器远比变异条件差。这些答案中的一些跳过的箍只是丑陋的。 我同意使用 i-1 索引器会更糟。【参考方案10】:

也许是这样?恕我直言,它清晰易读。如果 if(n>=1) 以某种方式隐式知道,则可以省略它。

if(n>=1) 
    // Start the loop at last index
    unsigned int i = n-1;
    do 
       // a plus: you can use i, not i-1 here
     while( i-- != 0 );

另一个版本:

if(n>=1) 
    unsigned int i = n;
    do 
       i--;

     while( i != 0 );

第一个没有 if 语句的代码如下所示:

unsigned int i = n-1;
do 

 while( i-- != 0 );

【讨论】:

【参考方案11】:

由于这不是标准的 for 循环,我可能会使用 while 循环,例如:

unsigned int i = n - 1;
while (1)

    /* do stuff  with i */

     if (i == 0)
    
        break;
    
    i--;

【讨论】:

【参考方案12】:

这是未经测试的,但你能做到以下几点:

for (unsigned int i, j = 0; j < n; i = (n - ++j)) 
    /* do stuff with i */

【讨论】:

【参考方案13】:
for (unsigned int i = n-1; i<(unsigned int)-1; i--)

好的,它的“模糊环算术”。

【讨论】:

甚至 -1u 而不是 (unsigned int)-1 我输入了 +1,但我不确定 -1u 是否总是导致 (unsigned int)(0-1)【参考方案14】:

或者,如果您需要从 n-1 到 0 的索引,您可以依赖 unsigned int 的包装行为

for(unsigned int i = n-1; i < n; i--) 
    ...

【讨论】:

【参考方案15】:

我提到这个选项的唯一原因是我没有在列表中看到它。

for ( unsigned int i = n-1; i < n; i-- ) 
... 

完全违背直觉,但它确实有效。它起作用的原因是因为从 0 中减去 1 会产生可以用无符号整数表示的最大数字。

总的来说,我认为使用无符号整数和算术运算并不是一个好主意,尤其是在减法时。

【讨论】:

这让我想知道在 for 循环中使用无符号算术是否是个好主意... 嗯……根据时间戳,@peje 比你早 3 分钟就到了……但我想知道你们两个是如何得到 exactly 相同的代码的???!!!【参考方案16】:
unsigned index;
for (unsigned i=0; i<n; i++)

    index = n-1 - i; // i == 0..n-1 => index == n-1..0

【讨论】:

【参考方案17】:

使用两个变量,一个用于计数向上,另一个用于数组索引:

unsigned int Index = MAX - 1;
unsigned int Counter;
for(Counter = 0; Counter < MAX; Counter++)

    // Use Index
    Index--;

【讨论】:

【参考方案18】:

简单,只需停在-1:

for( unsigned int i = n; i != -1; --i )

 /* do stuff with i */

编辑:不知道为什么这被否决了。它有效,而且比上述任何一种都更简单、更明显。

【讨论】:

它不起作用,因为i 是无符号的,您不应该将它与有符号值进行比较。我的 C 编译器有足够高的警告,不接受这样的代码。如果您使用像(unsigned)-1 这样的演员表,它会起作用,但为什么不使用更明显的UINT_MAX 宏呢?如果我在某人的代码中看到这一点,我会挠头试图弄清楚为什么它不是无限循环。它有效,但并不明显。 我绝对喜欢 UINT_MAX 解决方案甚至演员表。爱它!我曾经使用 Pete Kirkham 建议的方法,但这要好得多!【参考方案19】:

e.z:

#define unsigned signed

for ( unsigned int i = n-1; i >= 0; i-- )  ... 

【讨论】:

为什么我希望将 'unsigned' 更改为 'signed' 并失去所有语义? 啊哈哈,我见过的最佳巨魔答案。【参考方案20】:

为什么不简单:

unsigned int i = n;
while(i--)
 
    // use i

这符合问题正文中列举的所有要求。 它不使用任何可能无法通过代码审查或违反编码标准的东西。 我能看到的唯一反对意见是,如果 OP 真的坚持使用 for 循环而不是生成 i = (n-1) .. 0 的直接方式。

【讨论】:

在我看来,优雅而简单的解决方案,虽然有点晦涩难懂。一般来说,我会尽量避免使用 ++ 或 -- 运算符的代码,我必须三思而后行才能找出那里真正发生的事情。 @Auron 当我学习 C 时,理解 ++ii++ 之间的区别并不被认为是晦涩难懂的。时代怎么变了。有 19 人的支持,这被认为更容易for (unsigned i = n ; i-- &gt; 0 ; )...哦,好吧! 我学习 C 时也不是。我也认为最受好评的答案也有点晦涩难懂。两年多过去了,也许今天我不会像推荐的那样投票。尽管如此,我还是赞成你的回答,我建议while (i-- != 0) 作为编写循环条件的更清晰的方式。

以上是关于使用无符号索引执行反向“for”循环的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 for 循环中计数的最佳方法是啥? [复制]

打破 JavaScript 中嵌套循环的最佳方法是啥?

哪个是索引for循环的最佳方法?

将 ssh 隧道反向访问公司防火墙后面的系统的最佳方法是啥?

使用 python scikit-learn 执行无监督文本分类(聚类)的最佳算法是啥?

MATLAB for 循环索引的最佳实践