web-dev-qa-db-fra.com

Quelle est la meilleure façon de faire une boucle inversée 'for' avec un index non signé?

Ma première tentative de inverser pour la boucle qui fait quelque chose n fois était quelque chose comme:

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

Ceci échoue car dans l'arithmétique non signée i est garantie d'être toujours supérieure ou égale à zéro, d'où la la condition de boucle sera toujours vraie. Heureusement, le compilateur gcc m'a averti d'une "comparaison inutile" avant de devoir me demander pourquoi la boucle s'exécutait à l'infini.


Je cherche un moyen élégant de résoudre ce problème en gardant à l'esprit que:

  1. Ce devrait être une boucle en arrière.
  2. L'index de boucle doit être non signé.
  3. n est une constante non signée.
  4. Il ne devrait pas être basé sur l'arithmétique en anneau "obscure" d'entiers non signés.

Des idées? Merci :)

56
Auron

Que diriez-vous:

for (unsigned i = n ; i-- > 0 ; )
{
  // do stuff with i
}
94
Skizz
for ( unsigned int loopIndex = n; loopIndex > 0; --loopIndex ) {
    unsigned int i = loopIndex - 1;
    ...
} 

ou

for ( unsigned int loopIndex = 0; loopIndex < n; ++loopIndex ) {
    unsigned int i = n - loopIndex - 1;
    ...
} 
12
Lou Franco
for ( unsigned int i = n; i != 0; i-- ) {
    // do something with i - 1
    ...     
}

Notez que si vous utilisez C++ ainsi que C, utiliser! = Est une bonne habitude à prendre lorsque vous passez à l'utilisation d'itérateurs, où <= etc. peut ne pas être disponible.

11
anon

Pourquoi pas simplement:

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

Cela répond à toutes les exigences énumérées dans le corps de la question. Il n'utilise aucun élément susceptible d'échouer à la révision du code ou de violer une norme de codage. La seule objection que j'ai pu y voir est que l'OP a vraiment insisté sur une boucle for et non sur une manière simple de générer i = (n-1) .. 0.

9
idz
for ( unsigned int i = n; i > 0; i-- ) {
    ...  
    i-1 //wherever you've been using i   
}
8
vartec

J'aurais tendance à utiliser

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

c'est presque la même chose que la réponse de skizz, (il manque un décrément final inutile, mais le compilateur devrait optimiser cela), et passera en fait la révision du code. Chaque norme de codage avec laquelle j'ai eu à travailler n'a eu aucune mutation dans la règle conditionnelle.

8
Pete Kirkham

Peut-être de cette façon? À mon humble avis, son clair et lisible. Vous pouvez omettre le if (n> = 1) s'il est implicitement connu d'une manière ou d'une autre.

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 );
}

Une autre version:

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

    } while( i != 0 );
}

Le premier code sans instruction if ressemblerait à:

unsigned int i = n-1;
do {

} while( i-- != 0 );
5
anon
for (unsigned int i = n-1; i<(unsigned int)-1; i--)

OK, son "arithmétique en anneau obscur".

4
Eric Bainville

Ou vous pouvez vous fier au comportement d’habillage de unsigned int si vous avez besoin d'une indexation de n-1 à 0

for(unsigned int i = n-1; i < n; i--) {
    ...
}
4
peje
for ( unsigned int i = n; i > 0; i-- ) {
    unsigned int x = i - 1;
    // do whatever you want with x    
}

Certainement pas élégant, mais ça marche.

3
itsmatt

La seule raison pour laquelle je mentionne cette option est que je ne l'ai pas vue dans la liste.

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

Totalement contre l'intuition, mais ça marche. la raison pour laquelle cela fonctionne est que la soustraction de 1 à 0 donne le plus grand nombre pouvant être représenté par un entier non signé.

En général, je ne pense pas que ce soit une bonne idée de travailler avec des nombres entiers et arthmétiques non signés, en particulier lors de la soustraction.

3
Renze de Waal

Facile, arrêtez-vous à -1:

for( unsigned int i = n; i != -1; --i )
{
 /* do stuff with i */
}

edit: je ne sais pas pourquoi cela est voté. cela fonctionne et c'est plus simple et plus évident que tout ce qui précède.

2
Dave
for ( unsigned int i = n; i > 0; i-- ) {
    ...     
}

Devrait bien fonctionner. Si vous devez utiliser la variable i comme index dans un tableau, faites-le comme ceci:

array[i-1];
1
arul

Hm. Voici vos options:

  1. Utilisation i=0 comme condition de rupture - La boucle ne s'exécutera pas lorsque j'atteindrai 0, exécutez donc 1 itération du contenu de la boucle pour i=0 une fois la boucle terminée.
for ( unsigned int i = n-1; i > 0; i-- ) {
    doStuff(i);
}
doStuff(0);
  1. Dans la boucle, testez i=0 et break out. Non recommandé car vous testez maintenant la valeur de i deux fois dans la boucle. L'utilisation de la rupture dans une boucle est également considérée comme une mauvaise pratique.
for ( unsigned int i = n-1; i >= 0; i-- ) {
    doStuff(i);
    if (i=0) break;
}
1
Tim
unsigned index;
for (unsigned i=0; i<n; i++)
{
    index = n-1 - i; // {i == 0..n-1} => {index == n-1..0}
}
1
user80452

Comme ce n'est pas une norme pour la boucle, j'utiliserais probablement une boucle while à la place, par exemple:

unsigned int i = n - 1;
while (1)
{
    /* do stuff  with i */

     if (i == 0)
    {
        break;
    }
    i--;
}
0
starblue

Ceci n'est pas testé, mais pourriez-vous faire ce qui suit:

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

Utilisez deux variables, une pour compter p, et l'autre pour l'index du tableau:

unsigned int Index = MAX - 1;
unsigned int Counter;
for(Counter = 0; Counter < MAX; Counter++)
{
    // Use Index
    Index--;
}
0
Steve Melnikoff