Estoy depurando un código en este momento y me encontré con esta línea:
for (std::size_t j = M; j <= M; --j)
(Escrito por mi jefe, que está de vacaciones).
Me parece realmente extraño.
¿Qué hace? Para mí, parece un bucle infinito.
Estoy depurando un código en este momento y me encontré con esta línea:
for (std::size_t j = M; j <= M; --j)
(Escrito por mi jefe, que está de vacaciones).
Me parece realmente extraño.
¿Qué hace? Para mí, parece un bucle infinito.
Mpasa si es el valor máximo para size_t? ¿Sigues pensando que es inteligente?
#define TRUE FALSEy vete de vacaciones.
Respuestas:
std::size_testá garantizado por el estándar C ++ para ser un unsignedtipo. Y si disminuye un unsignedtipo desde 0, el estándar garantiza que el resultado de hacerlo es el valor más grande para ese tipo.
Ese valor envuelto siempre es mayor o igual a M1, por lo que el ciclo termina.
Entonces, j <= Mcuando se aplica a un unsignedtipo, es una forma conveniente de decir "ejecutar el ciclo a cero y luego detenerse".
Existen alternativas como ejecutar juno más grande de lo que desea, e incluso usar el operador de diapositiva for (std::size_t j = M + 1; j --> 0; ){ , que posiblemente son más claras, aunque requieren más escritura. Sin embargo, supongo que una desventaja (aparte del efecto desconcertante que produce en la primera inspección) es que no se adapta bien a lenguajes sin tipos sin firmar, como Java.
Tenga en cuenta también que el esquema que ha elegido su jefe "toma prestado" un valor posible del unsignedconjunto: en este caso sucede que el Mconjunto a std::numeric_limits<std::size_t>::max()no tendrá el comportamiento correcto. De hecho, en ese caso, el bucle es infinito . (¿Es eso lo que está observando?) Debería insertar un comentario en ese sentido en el código, y posiblemente incluso afirmar sobre esa condición en particular.
Mno ser std::numeric_limits<std::size_t>::max().
std::numeric_limits<std::size_t>::max()entonces M + 1será cero y el for (std::size_t j = M + 1; j --> 0; )bucle no formará bucle.
size_tsea de 64 bits, se necesitarán varios cientos de años para observar el comportamiento incorrecto en el caso de borde. (Excepto si el optimizador puede deshacerse del bucle).
Lo que su jefe probablemente estaba tratando de hacer era contar hacia atrás desde Mcero inclusive, realizando alguna acción en cada número.
Por desgracia hay un caso extremo en que habrá de hecho le dará un bucle infinito, aquella en la que Mes el máximo size_tvalor que puede tener. Y, aunque está bien definido lo que hará un valor sin firmar cuando lo disminuyas desde cero, mantengo que el código en sí es un ejemplo de pensamiento descuidado, especialmente porque hay una solución perfectamente viable sin las deficiencias del intento de tus jefes.
Esa variante más segura (y más legible, en mi opinión, sin dejar de mantener un límite de alcance ajustado), sería:
{
std::size_t j = M;
do {
doSomethingWith(j);
} while (j-- != 0);
}
A modo de ejemplo, consulte el siguiente código:
#include <iostream>
#include <cstdint>
#include <climits>
int main (void) {
uint32_t quant = 0;
unsigned short us = USHRT_MAX;
std::cout << "Starting at " << us;
do {
quant++;
} while (us-- != 0);
std::cout << ", we would loop " << quant << " times.\n";
return 0;
}
Esto hace básicamente lo mismo con un unsigned shorty puede ver que procesa todos los valores:
Starting at 65535, we would loop 65536 times.
Reemplazar el do..whileciclo en el código anterior con lo que hizo básicamente su jefe resultará en un ciclo infinito. Pruébelo y vea:
for (unsigned int us2 = us; us2 <= us; --us2) {
quant++;
}
0. ¿Por qué no for(size_t j = M; j-- != 0; )?
numeric_limits<size_t>::max().
M == 0dará como resultado que el elemento 0 no se procese, por lo que tiene un caso de borde. El uso de mi do..whilemétodo de verificación posterior elimina el caso de borde por completo. Si lo prueba con M == 1, verá que hace 1 y 0. De manera similar, comience con max_size_t(lo que sea que sea) y comenzará con éxito en ese punto y luego disminuirá hasta cero inclusive.
exitif”, es decir { j =M; for(;;){ f(j); if( j == 0 )break; j -= 1; } }. Incluso podría ser necesario reemplazar el breakcon a gotosi las cosas se anidan, ya que C no tiene bucles con nombre. Si "estructurado" significa "susceptible de razonar sobre fragmentos", está estructurado (¡el diseño ayuda!), Y también si significa "susceptible de validación formal mediante el razonamiento sobre condiciones previas y posteriores". Aunque en este caso j--funciona, el estilo exitif puede estar justificado cuando se requiere una transición más complicada.
size_tno está firmado, por lo que se garantiza que se ajustará a su valor máximo cuando se intente desaprobar el cero, finalizando el ciclo. Sin embargo, sigue siendo un código horrible.