C - Tienda de ultramarinos
logo Tienda de Ultramarinos
Etiquetas: C

Incremento y decremento

Hoy toca post para informáticos. Como bien sabréis, muchos lenguajes disponen de una forma abreviada de indicar incrementos y decrementos, que son ‘++‘ y ‘--‘. Esto, además de facilitar la vida a los programadores evitándoles escribir a=a+1, es también de utilidad para las optimizaciones que hace el compilador, puesto que en la mayoría de arquitecturas existen instrucciones de incremento y decremento que se ejecutan utilizando menos recursos que una suma convencional.

En algunos lenguajes, como en C y Java, se puede utilizar este operador delante y detrás de las variables, pero tiene un comportamiento distinto. Si el operador está delante, se hace primero la resta y se evalúa el resultado; mientras que si está detrás, primero se devuelve el valor, y después se realiza la resta.

Así, si b=10 y hacemos a=--b, a y b terminarán con valor 9, pero a=b-- terminará con a=10 y b=9.

Bien, recordado esto, en mis apuntes de compiladores se menciona como un código algo lioso a primera vista, la siguiente instrucción c=a---b--. A raíz de ello, he decidido pensar en  situaciones similares a ésta, que son:

c = --a---b;
c = --a-b--;
c = a-----b;
c = a---b--;
c = a---b;

Suponiendo que es ilegal --n--, y obviando la última (hay que acordar cómo se comporta) ¿Alguien se anima a hacer la traza? Es facilita, la hice ayer a mano en lugar de seguir estudiando :P.

Aunque todas salvo la última parezcan correctas y sin ambigüedades, curiosamente hay dos de ellas que ni C ni Java aceptan (con los compiladores GCC 2.8.1, Borlandc 3.0 y JDK 1.6). Son la primera y la tercera, y ocurre lo mismo si en lugar de ‘--‘ utilizamos ‘++‘. ¿Por qué? Pues imagino que por el tipo de análisis que hace el compilador.

Tal y como veo que se comporta (he hecho más pruebas aparte de esos 5 casos), cuando el analizador léxico lee el código carácter a carácter, siempre que encuentra dos ‘-‘ seguidos, los considera un único token ‘--‘ y así es como los envía al siguiente nivel.

Después, en el analizador sintáctico/semántico debe de existir algún tipo de precedencia asignada al operador ‘--‘ por encima de la resta y el opuesto, de modo que cuando se encuentra con --a, ó a--, hace directamente una reducción y pasa a considerar la expresión como una constante, a la que ya no tiene sentido volver a aplicar otro ‘--‘.

En el caso “especial” c=a---b, a mi juicio el único que es ambiguo y del que debería quejarse el compilador, se opta por aplicar el operador ‘--‘ a la variable a; siendo equivalente a ejecutar c=a-b y después a--.

También acepta correctamente c=-a--; // c = -a (y decrementar después a).

Otras asignaciones que dan error de compilación, y comentado a lo que debería corresponder:

c = ---a; //c = – (decremento de a)

c = --a--b; //c = (decremento de a) menos (-b)

c = a--b; // c = a menos (-b)

c = a----b; // c = a menos (-b) [y después decrementar a]

Por los errores que da el compilador, en el primer caso creo que intentar aplicar el operador ‘--‘ sobre ‘-‘, y en el tercer caso, evalua ‘a--‘, y después encuentra la b sin operadores entre ellos. En el segundo fallan dos cosas: primero aplica correctamente --a, pero al resultado no se le puede aplicar otra vez ‘--‘; el segundo fallo es que ha consumido el token ‘--‘ y se encuentra, de nuevo, con una b solitaria sin operadores entre la a. En el cuarto tenemos otros dos fallos similares al anterior.

¿Ocurrirá así en todos los compiladores de C, o habrán tenido esto en cuenta en algún caso? ¿O es algo definido directamente en el estándar? He echado un vistazo rápido al ANSI C buscando “decrement”, pero no he leído nada destacable.

Los listos del C

Tengo una hipótesis que espero poder convertir pronto en teoria.

En toda carrera de informática hay al menos un listo del C por curso. Los listos del C son esos tipos que presumen de llevar toda su puta vida programando en C y que se lo conocen al dedillo. Son los típicos que tienen que tener la última palabra en toda discusión con el profesor sobre algún tema al respecto, y que si no pueden lo critican al final de la clase afirmando: “Yo sé más de C que él“.

Que probablemente sabrán un huevo, porque seguro que han programado tanto que cuando escriben la dirección de su casa le cascan un & delante, pero la actitud que adoptan es del típico listillo que a nadie le gusta.

Algunos llegan de módulos, y otros simplemente han bajado demasiados manuales de internet o hecho varios cursos. Supongo que por éstos últimos podemos ampliar la teoría también a los módulos.

¿Y vosotros? ¿Sois “Los listos del C” de vuestra clase o conocéis a alguno?

gotoxy(x,y) y clrscr() de C sobre Linux

Se me había ocurrido compilar algunos de los programitas que hice hace dos o tres años en C en Windows y claro, ahora en Linux daban algunos fallos por librerías y funciones utilizadas. Lo normal.

Sin embargo, me ha llamado la atención de que no funcionaran ni gotoxy(x,y), ni clrscr(). La primera para ir a unas coordenadas de la pantalla para poder escribir a continuación allí, en lugar de línea a línea, y la segunda para limpiar toda la pantalla. Y da la casualidad de que ayer me aburría y me apeteció programarme un Juego de la Vida de Conway, de modo que necesitaba una función que sustituyese al gotoxy(x,y).

Así pues he intentado mirar en algún manual y luego he googleado un poco. Parece ser que estas dos funciones sólo están en librerías del DOS, y que no hay equivalente en librerías existentes en Linux. Hay bastantes hilos en foros de gente preguntando por eso mismo, y en algunos la solución que dan es definir esas funciones en tu propio código. Por supuesto, acompañan el código.

El clrscr() no puede ser más simple: un bucle con tantos intros como desees, unos 50 serán más que suficientes, aunque todo depende del tamaño de la ventana. ¿Para qué complicarse más la vida?

Para el gotoxy(x,y) ya es necesaria una secuencia de escape, la \033:

Escape sequences allow you to send nongraphic control characters to a display device. For example, the ESC character (\033) is often used as the first character of a control command for a terminal or printer. Some escape sequences are device-specific. For instance, the vertical-tab and formfeed escape sequences (\v and \f) do not affect screen output, but they do perform appropriate printer operations.

He encontrado el código para la función gotoxy(x,y) en dos sitios, uno más largo y otro más reducido. He comprobado que funcionan ambas. Copio aquí el reducido:

#define MAX_SCREEN_AREA 100
int gotoxy(int x, int y){
char essq[MAX_SCREEN_AREA]={0}; // String variable to hold the escape sequence
sprintf(essq, "\033[%d;%df", y,x);
printf("%s", essq);
return 0;
}

De todas formas, he descubierto que no es necesaria tanta línea. Analizando el código que visto cómo funcionaba y he probado a incluir la secuencia de escape directamente en un printf() y, voilá, funciona. Supongo queirá también con scanf() y otros.

printf("\033[9;9f asdf\n");