ti-enxame.com

Iteração reversa com uma variável de loop não assinada

Eu tenho discutido o uso de size_t com colegas. Um problema que surgiu foi o loop que diminui a variável do loop até atingir zero.

Considere o seguinte código:

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

Isso causa um loop infinito devido a um invólucro inteiro não assinado. O que você faz neste caso? Parece muito fácil escrever o código acima e não perceber que você cometeu um erro.

Duas sugestões da nossa equipe são usar um dos seguintes estilos:

for (size_t i = n-1; i != -1 ; --i) { ... }

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

Mas eu me pergunto que outras opções existem ...

60
Daniel Paull

Pessoalmente, passei a gostar:

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

Não tem um engraçado -1, b) a verificação da condição é mnemônica, c) termina com um smiley adequado.

83
visitor

Números inteiros não assinados são garantidos para contornar bem. Eles apenas implementam o módulo aritmético 2N. Portanto, um idioma de fácil leitura é este:

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

isso define a variável para o valor inicial que você deseja, mostra o sentido da iteração (para baixo) e fornece precisamente a condição nos valores que você deseja manipular.

55
Jens Gustedt
  1. Substitua o loop por um algoritmo.
  2. Use um iterador reverso em vez de um número inteiro.
  3. Contagem decrescente de n a 1, mas dentro do loop use i-1 em vez de i.
9
Jerry Coffin

Você está usando contêineres de biblioteca padrão? Se sim, eu gosto de reverse_iterator

   vector<int> ivect;

   // Push, Push, Push...

   vector<int>::reverse_iterator riter;
   for(riter=riter.rbegin(); riter!=ivect.rend(); ++riter)
   {
       //...
   }

Para uma matriz bruta, você pode simplesmente usar um std::reverse_iterator a chave para isso é que um ponteiro é um iterador:

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

A iteração de objetos não contíguos pode ser feita armazenando os ponteiros de objeto em um contêiner ou matriz:

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

Se você realmente quer usar uma roupa nua size_t então por que usar todo esse truque -1 implicitamente confuso nas outras respostas? O valor máximo de size_t está explicitamente disponível para uso como seu valor de finalização:

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
5
joshperry

Se você estiver preocupado em escrever acidentalmente um loop como esse, alguns compiladores alertarão sobre essas coisas. Por exemplo, o gcc tem um aviso ativado pelo -Wtype-limits opção (também ativada por -Wextra):

x.c:42: warning: comparison of unsigned expression >= 0 is always true
5
caf

i != -1 confia no -1 sendo silenciosamente convertido em um size_t, o que me parece frágil, portanto, das alternativas que você apresenta, eu definitivamente aceitaria a alternativa pós-decremento. Outra possibilidade (especialmente se você realmente não precisa de i no corpo do loop, mas precisa iterar em uma matriz na ordem inversa) seria agrupar a matriz em um std::- como contêiner e use um iterador no wrapper, com os métodos rbegin e rend. Por exemplo, Boost.Array suportaria a última opção.

3
Alex Martelli
size_t i = n-1;

do  { 
  ...
} while ( i-- != 0);

Você pode agrupar isso com uma if (n > 0), se necessário.

1
Ring Ø

Outra maneira (sem comparações assinadas/não assinadas):

for (size_t i = n-1; i + 1 > 0; i--)

desde a

(i + 1 > 0) === (i > -1)
1
boni

Aqui está um ponteiro para boa discussão sobre este tópico.

Eu tentaria:

for( size_t i = n; i != 0; i-- ) {
  // do stuff with array[ i - 1 ]
}
1
Arun

Outra solução (disponível em sistemas compatíveis com POSIX) que achei simples e eficaz é substituir size_t por ssize_t:

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

Em sistemas não POSIX, ssize_t não é difícil de digitar: Alternativa para ssize_t em sistemas não conformes POSIX

0
Corentor