Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


Directivas  #ifdef,   #ifndef

§1  Sintaxis

#ifdef identificador
#ifndef identificador

§2  Descripción

Las directivas  #ifdef y #ifndef son condicionales especializadas para comprobar si un macro-identificador está definido o no. La mecánica es la misma que con #if, #elif, #endif ( 4.9.10d).


  Tenga en cuenta que un macro-identificador X se define con #define X y se indefine con #undef X ( 4.9.10i), con lo que podemos controlar a voluntad la zonas de código en que se considera definido e indefinido.

Nota:  La existencia semántica del identificador (el hecho de que esté definido o no), es independiente del valor concreto que pudiera tener asignado. Como veremos a continuación, incluso si se le asigna el valor nulo, el identificador se considera definido.

Un identificador definido como NULL es considerado definido. Por ejemplo:

#define nulo NULL

§3  Ejemplo

#define isSp        // Flag Español/Inglés

#ifdef  isSp

   # include "ztr--Sp.CH"

   #define VComP "C.000621Sp"

#else

   # include "ztr--Us.CH"

   #define VComP "C.000621Us"

#endif

....

....

#undef isSp         // A partir de aquí, isSp se considera indefinido

....

....

§4  Directivas de guarda

Tenga en cuenta que el lenguaje C++ es muy proclive a que se cometan cierto tipo de errores al trabajar con ficheros de cabecera (es seguro que ocurrirá a menudo). Por ejemplo, supongamos que tenemos una clase denominada Hotel que utiliza una clase denominada Suite y que cada una de estas clases utiliza el mismo fichero de cabecera, por ejemplo ZonasHotel.h [2]. En este caso es más que posible que recibamos un error de compilación del tipo

... Multiple declaration for 'FILE' ...

indicándonos que estamos redefiniendo los símbolos de ZonasHotel.h la segunda vez que la cabecera es incluida.

Una solución es disponer el código como se indica:

#ifndef zonasH
   #include "ZonasHotel.h"
   #define zonasH            // Nota [3] 
#endif
...    // aquí sigue el código del programa

Sin embargo, esta disposición tiene el inconveniente de que tenemos que acordarnos de definir zonasH cada vez que vayamos a incluir la cabecera. Si lo olvidamos, volverá a producirse el error (esto es muy probable en ambientes donde trabajan varios programadores en el mismo proyecto).

Una solución más elegante, que recomendamos, es utilizar la disposición anterior dentro del propio fichero de cabecera. En nuestro caso, el fichero ZonasHotel.h tendría el siguiente aspecto:

#ifndef zonasH

   ... contenido del fichero

   #define zonasH
#endif


Si analizan con detenimiento los ficheros de cabecera de su compilador C++, comprobará que las directivas descritas en este epígrafe son utilizadas con profusión, y como a pesar de que están llenas de declaraciones, la inclusión dos veces de una misma cabecera no produce ningún error de compilación. La razón es que los fabricantes de compiladores utilizan el truco anterior. Como ejemplo, tomamos la cabecera <stdio.h> del compilador Borland C++ 5.5, que incluye una gran sección cuyo aspecto esquemático es el siguiente:

...
#ifndef __STDIO_H
  #define __STDIO_H
  ....
  typedef struct {
    ...
  } FILE;
  ...
#endif    // __STDIO_H
...

Si se coloca la directiva #include <stdio.h> en un fuente, la primera vez que el preprocesador pasa por la directiva ifndef, la macro __STDIO_H, que no está definida [1], se define en la siguiente línea, así como la estructura FILE. La segunda vez que se incluyera esta cabecera, el preprocesador se encontraría con que la macro __STDIO_H ya está definida, por lo que se saltaría toda la sección hasta el endif correspondiente. De no haberse hecho así se obtendría el error antes mencionado. Por esta razón, este tipo de directivas condicionales, que se utilizan para que no se produzcan declaraciones múltiples, se denominan directivas de guarda.

Nota: el compilador MS VC++ dispone de una pragma ( 4.9.10i) específica para este fin: #pragma once. Sirve para indicar al compilador que el fichero en que reside dicha directiva, debe ser incluido solo una vez en cada compilación. Por supuesto esta directiva se coloca dentro del fichero de cabecera que deseamos no se repita. La forma de uso es la siguiente:

//Fichero ZonasHotel.h que deseamos no se repita
#pragma once
... contenido del fichero


Otra posibilidad para formar directivas de guarda es la utilización del operador defined ( 4.9.10d). Por ejemplo, el código que sigue comprueba si la constante simbólica ZONASH ha sido definida. Si no lo ha sido, se define para veces sucesivas y se compila el código subsiguiente. En caso contrario (si el código ya ha sido incluido en la compilación), todo el bloque es ignorado por el compilador.

// ZonasHotel.h  Ejemplo de fichero de cabecera

#if !defined( ZONASH )
  #define ZONASH

  class MiClase {
    ...
  };

#endif

  Inicio.


[1]  Hemos señalado repetidamente que los nombres con guión bajo inicial deben ser reservados para uso exclusivo del compilador, por lo que no debemos utilizarlos en nuestros fuentes.

[2]  Es muy frecuente que las definiciones de clases se incluyan en ficheros de cabecera.

[3]  Una expresión de este tipo, sin un valor a la derecha, es interpretada por el preprocesador como de valor 1, que es cierto (true).