El lenguaje C (y C++) ofrece tres maneras de definir constantes. En este artículo queremos aclarar las diferencias entre #define, enum y const. Como veremos a continuación, casi siempre es preferible usar const.

#define CONSTANTE_DEFINE 5
enum T_ENUM { CONSTANTE_ENUM = 5 };
const int CONSTANTE_VARIABLE = 5;

Los tres valores CONSTANTE_DEFINE, CONSTANTE_ENUM y CONSTANTE_VARIABLE valen lo mismo (tienen el valor 5) pero tienen diferencias significativas a la hora de compilación.

La directiva #define es una palabra clave del pre-procesador. Técnicamente se podría decir que no forma parte del lenguaje C. Antes que el compilador ve el código, el pre-procesador ya a reemplazado la macro CONSTANTE_DEFINE por su definición – en nuestro caso el símbolo 5. Es decir, en todos los sitios donde aparece el identificador CONSTANTE_DEFINE, el compilador ve un 5 como si nosotros mismos hubiéramos escrito un 5 en estos sitios. Es posible que el compilador ni siquiera puede decirnos qué exactamente está mal en una compilación fracasa, porque no sabe nada del identificador CONSTANTE_DEFINE.

Como la directiva #define sólo tiene un significado textual y carece de cualquier información de tipo, es aconsejable evitar estas directivas y const siempre y cuando sea posible. Sin embargo, el hecho que #define no tiene un tipo asociado puede ser también una ventaja a la hora de hacer conversiones de tipos implícitas. No obstante, todavía es cuestionable si conversiones implícitas son deseables.

Los programadores saben que físicamente se codifica una constante definida con enum como un const intconceptualmente una constante definida con enum no tiene valor numérico asociado. El concepto es todo al contrario: usar identificadores más significativos como LUNES, MARTES, AZUL, VERDADERO en lugar de números en que uno tiene que saber que un “1” significa lunes, otro “1” azul y un tercero verdadero.

Por lo dicho conviene recordar que no hay nada mejor para representar el concepto de uno que el símbolo “1”. Si queremos añadir un número específico entonces mejor ponemos este número en lugar de inventarnos una constante cuyo valor luego debemos buscar por ahí.

La razón por qué se pueden asignar valores específicos a constante de enumeración es para poder adaptarlas a valores exteriores. Por ejemplo, podría definir un tipo que encapsula constantes de error de HTML.

enum T_HTML_ERROR
{
    E_PAGE_NOT_FOUND = 404;
}

Por asignar de forma inteligente los valores a las constantes puedo asignar fácilmente un valor de tipo int a una variable de tipo T_HTML_ERROR mediante un cast. Por ponerle el nombre E_PAGE_NOT_FOUND al valor 404, será más fácil entender el código.  Sin embargo, todavía debo hacer un cast y por eso, en general, es un mejor estilo definir constantes con const int que luego se asignan a una variable de tipo int. Como ya hemos dicho, la idea de una constante enum es justamente no tener en cuenta su valor.

Una constante enum como LUNES no es completa en sí, sino forma parte del conjunto de constantes que forman un tipo de enumeración como T_DIA. Desde el punto de vista del lenguaje no tiene sentido asignar una constante a otra cosa que a una variable de este tipo de enumeración. En lenguajes más puristas como el Pascal es incluso prohibido convertir enumerativos a su representación numérica interna.

Finalmente queda la opción de definir constantes mediante const. Este método permite definir el tipo y el valor de la constante. Es incluso posible esconder el valor de la constante, definiéndola extern en el fichero de cabecera y asignar su valor en el fichero fuente. Aunque codificar constantes con const es la manera más limpia, suele ser la menos usada. La razón será más bien costumbre porque técnicamente hay poca razón de usar #define o enum.

El compilador puede comprobar el tipo de las constantes con const que las hace mejor que los #define. Al mismo tiempo permite las misma conversiones implícitas. Si no tengo claro si quiero codificar un número como entero o una cadena de texto, puedo usar un typedef para poder modificar también el tipo de la constante con facilidad.

Los enum sólo pueden representar valores enteros, mientras una constante puede tener cualquier tipo: un número flotante, una cadena de texto, una estructura compleja. Todo. Una constante const tiene una dirección de memoria, por lo cual se puede pasar un puntero a ella. El valor de una constante const puede ser público en el fichero de cabecera o escondido en el fichero fuente.

En fin, hemos visto las diferencias y concluimos que por regla general es preferible usar constantes declaradas por const. Las excepciones son:

  • Podemos usar #define cuando efectivamente necesitamos hacer algo antes de compilar. Por ejemplo, constantes de error que dependen de la plataforma, en general dentro de una compilación condicional con #if#endif.
  • Podemos usar enum si sólo queremos nombres sin necesidad de saber los valores con qué se representan internamente.

Por todo lo demás: const

Referencias

Anuncios