La maravilla del lenguaje C es que en ocasiones pasan cosas inexplicables que hablen la puerta al misterio y la superstición. Esa es la razón por la que es bastante común que los programadores C muestren sus programas en noches de luna llena tras sacrificar una cabra y dos gallinas.
Si, C es así, a veces ocurren fenómenos inexplicables y mientras sean inexplicables pensaremos que esos hechos están controlados por entes superiores, Dioses del caché o semidioses de la laguna oscura de la pila. Pero una vez que encuentras la explicación… simplemente se convierten en bugs.
Bucles infinitos
Antes de mostraros nuestro bucle infinito modo esotérico vamos a ver las distintas formas en las que podemos hacer un bucle infinito en C.
Por cierto, un bucle infinito es una parte de un programa que se ejecuta una y otra vez sin fin… bueno, si, es exactamente lo que su nombre indica. Normalmente, dentro del bucle suele haber una condición de parada, pero en ocasiones la única forma de pararlo es matando el proceso.
Bucle infinito while
La primera forma de crear un bucle infinito y, probablemente la más
común y evidente es utilizar un bucle while
de la siguiente
forma:
...
while (1) {
("Me más que el chorizo");
puts }
Puesto que la condición en el while
es siempre cierta,
todo lo que pongamos en su interior se repetirá hasta que interrumpamos
el programa.
Podemos poner el número que más rabia nos de en el
while
, mientras sea distinto de 0
, la
condición se considerará verdadera y el bucle no parará de
ejecutarse.
Bucle infinito for
Otra forma, menos habitual de implementar bucles infinitos es
utilizando for
de la siguiente forma:
for (;;) {
("Me repito más que el chorizo");
puts }
En este caso, al no pasar ninguna condición se toma por defecto el valor true. La expresión anterior es equivalente a:
for (;1;) {
("Me repito más que el chorizo");
puts }
Bucle infinito goto
Este si que no lo vais a ver en el mundo real, ya que todo el mundo
considera los goto
s peligrosos, si bien, tanto los bucles
while
como los for
se implementan en
ensamblador con un goto
o salto. Podemos escribir un bucle
infinito con goto
de la siguiente forma:
:
bucle("Me repito más que el chorizo");
puts goto bucle;
Bucle infinito esotérico
Para terminar os dejamos una forma de implementar un bucle infinito un tanto esotérica. Veámoslo y luego lo discutimos.
for (unsigned char i = 0; i < 256; i++)
("Me repito más que el chorizo"); puts
Puesto que nosotros queremos hacer un bucle infinito, nuestro programa es perfectamente válido, sin embargo cuando vemos algo como esto es habitual que la intención del programador no fuera programar un bucle infinito.
De hecho esto es algo que me pasó hace muchos años cuando empezaba a programar en C. Aquí esta la historia:
Cuando empiezas a programar en C, ves todos esos tipos con distintos tamaños e intentas ser guay y usar el tipo que mejor se adecue a lo que tienes que hacer. Así que si vas a hacer un bucle de unas pocas interacciones, lo suyo es utilizar un byte como contador para no desperdiciar memoria. Luego aprendes, muchos años después que realmente no estas ahorrando nada… pero eso es otra historia.
Pues resulta que estaba programando algún tipo de algoritmo de procesado de señal. No recuerdo exactamente lo que era, pero en algún lugar había un pequeño bucle de 32 o 64 iteraciones. Todo funcionaba bien, hasta que aumenté el valor a 128 y de repente mi programa dejó de funcionar.
Recuerdo que me llevó algún tiempo reaccionar. Pensaba: “el programa funcionaba hace 2 minutos y no he cambiado el algoritmo¨. Usé el depurador y me llevo un rato entender porque cuando llegaba a la última interacción mi contador se reseteaba y el bucle comenzaba a empezar.
En aquel momento estaba usando un contador char
que en
los 90 era con signo por defecto (esto ha ido cambiando varias veces en
las últimas décadas) y por lo tanto, al llegar a 128 se producía un
overflow y el siguiente valor pasaba a ser -127 en lugar de 129.
El bucle infinito esotéterico explicado
En el código de arriba pasa exactamente lo mismo. i
es
un unsigned char
que toma valores entre 0
y
255
. En la última iteración, cuando i
vale
255
for
hace la comparación
i < 256
la cual es falsa, ya que i
todavía
es 255
, así que ejecuta el bucle una vez más.
Al terminar el bucle, for
ejecuta la última instrucción
i++
. Como i
es 255
al sumarle uno
pasa a ser 256
pero ese valor no se puede almacenar en un
byte (unsigned char
) con lo que solo los 8 bits menos
significativos se asignan a la variable i
y por lo tanto
toma el valor 0
el cual es menor que 256
y por
lo tanto el bucle continua infinitamente.
Conclusión
Bueno, la conclusión más importante es que debemos ser consciente del tamaño de nuestras variables y evitar que se produzcan desbordamientos que pueden provocar… fenómenos extraños….. uuuuuuhhhhhhh
■