Precedencia de operadores

Conozco poca gente que se aprende de memoria la precedencia de los operadores. La solución simple es siempre usar paréntesis. Para los curiosos, hagan un pequeño programa de prueba para comprobar sus sospechas.

#include <stdio.h>

int main() {
unsigned char c,b;

c=0xF0u; // Mascara..
b=0xCAu; // Valor..

if(c & b == 0xC0u) {
printf("Esta condicion no se cumple\n");
}
if((c&b) == 0xC0u) {
printf("Esta condicion se cumple\n");
}
return (1);
}

Comparaciones con constantes

Esta es una sugerencia que a mucha gente no le gusta. Supongamos que en algún momento delaramos:

#define CONSTANTE 5

Al hacer una comparación de igualdad de esa constante con una variable, la mayoría de la gente escribirá:

if(a==CONSTANTE) ...

Sin embargo, un error muy común es un solo signo de igualdad en lugar de 2:

if(a=CONSTANTE) ... // Esto siempre será verdadero

Por ello, algunas personas sugieren que se escriba al revés:

if(CONSTANTE==a) ...

En caso de omitir un signo de igualdad, es el compilador quien marcará error (no se puede asignar un valor a una constante).

Ciertamente no es tan transliterable. Quienes se oponen dicen que en la primera forma decimos "si a es igual a CONSTANTE" mientras que en la segunda resulta que no es tan intuitivo leer "si CONSTANTE es igual a a"; siendo que es "a" lo que nos interesa comparar.


Ciclos

La mayoría de los ciclos son de la forma:

for(i=0;i<N;i++)...

Y sabemos que el ciclo se va a ejecutar N veces. Cuando se utilizan índices distintos -- por ejemplo for(i=1;i<=N;i++) --, aumenta la probabilidad de errores. Esto no quiere decir que debamos evitarlos (especialmente si, por ejemplo, optimiza el código), pero al buscar errores en el código, estos son buenos lugares para revisar.


Evitar "números mágicos"

Esta es una recomendación "de cajón". Si tu código tiene números mágicos, es propenso a errores. A veces decidimos usar 64 coeficientes en lugar de 32 (digamos para mayor precisión) y "se nos pasa" actualizarlo en todo el código. Esto es muy simple de solucionar:

#define NUM_COEFS 32
...
for (i=0; i<NUM_COEFS; i++) ...


Defines

Los "defines" pueden ser muy útiles, pero hay que tener cuidado con ellos. Por ejemplo, si tenemos:

#define SUMA(A,B) A+B

Podemos incurrir en un grave error si hacemos:

w = x * SUMA(y,z);

Este enunciado, nos da a entender que la suma de "y" y "z" lo multiplicaremos por "x"; pero esto se traduce en:

w = x * y + z;

Y puesto que la multiplicación tiene precedencia, el resultado será:

w = (x*y) + z;

Usando paréntesis nos quitamos de este problema:

#define SUMA(A,B) (A+B)

O incluso mejor:

#define SUMA(A,B) ((A)+(B))

¿Porqué? Porque evitaríamos errores de este tipo:

SUMA(8>>1,5)

Aquí supondríamos que 8>>1=4 se va a sumar a 5, resultando en 9. Pero en realidad, si no usamos paréntesis extra alrededor de A y B, esto se expandería a (8>>1+5); y como el operador '+' tiene mayor precedencia que '>>', se reduciría a (8>>(1+5)) = (8>>6) = 0.


Corchetes

Aunque a veces parece innecesario, es muy importante poner corchetes en todos los ifs, whiles, etcétera.

Mucha gente dice que es excusable si se tiene buena indentación:

if(x==y)
return TRUE;

Sin embargo, es propenso a errores; ya que es fácil que alguien inserte una línea adicional:

if(x==y)
printf("X es igual a Y\n");
return TRUE; // Esto siempre se ejecuta!!!
return FALSE; // Esto nunca se ejecuta!!! 

Otro buen argumento es el hecho de que no siempre queda claro qué hará un código como el siguiente:


if(1==a)
if(2==c)
printf("c=2\n");
else
printf("c!=2? a!=1? \n");

Y la indentación sólo nos confundirá si comparamos el ejemplo anterior con lo siguiente:


if(1==a)
if(2==c)
printf("c=2\n");
else
printf("c!=2? a!=1? \n");

En lo personal, yo sólo omito corchetes cuando pongo el enunciado en la misma línea:

if(x<1) return FALSE;

Así es menos propenso a errores el código.


Comentar grandes bloques de código

Diferentes compiladores manejan de distinta forma los comentarios anidados.

Claro que en primer lugar no debería haber comentarios anidados, pero es una cosa más que es propensa a errores.

/* codigo de prueba */
for(i=0; i<NUM_SAMPLES; i++)  /*recorre el arreglo*/ {
    InvierteValores(buffer[i]);
}
...

Podemos cometer un grave error si comentamos todo.. El compilador probablemente así (ver colores):

/* codigo de prueba */
/*
for(i=0; i<NUM_SAMPLES; i++)  /*recorre el arreglo*/ {
    InvierteValores(buffer[i]);
}
...
*/

En donde se seguiría llamando a invierteValores(). O probablemente así:

/* codigo de prueba */
/*
for(i=0; i<NUM_SAMPLES; i++)  /*recorre el arreglo*/ {
    InvierteValores(buffer[i]);
}
...
*/

Esto es probablemente lo que se desea, pero no se puede confiar en ello. Asi que podría causar estragos al tratar de depurarlo. Tratar de cuidar todos los comentarios al comentar bloques, eventualmente nos puede confundir. Especialmente si ya tenía código comentado:

/* codigo de prueba */
/*
for(i=0; i<NUM_SAMPLES; i++)  */ /*recorre el arreglo*/ /* {
    InvierteValores(buffer[i]);
*/ /* printf("%d"buffer[i]); */ /*
*/ /* ValidaValores(buffer[i]); */ /*
}
...
*/

Aparentemente lo más recomedable es usar el preprocesador:

/* codigo de prueba */
#if 0
for(i=0; i<NUM_SAMPLES; i++)  /*recorre el arreglo*/ {
    InvierteValores(buffer[i]);
/* printf("%d"buffer[i]); */
/* ValidaValores(buffer[i]); */
}
...
#endif

De esta forma, nuestro código es más seguro y es mas fácil de depurar.


[ Regresar ]