C/Visual C - Directivas del Preprocesador

 
Vista:

Directivas del Preprocesador

Publicado por Santi (1 intervención) el 04/06/2006 00:39:54
Hola a todos.

Vereis, estoy siguiendo un libro de C++, y he llegado a la parte de las directivas del preprocesador...
El problema es que no lo explica muy bien, y no se para que sirven; me parecen igual que las variables y no les noto diferencia (me refiero a los #define y tales).

Por favor si alguien me explicara un poco el uso de esto le estaría muy agradecido.

Saludos!
Valora esta pregunta
Me gusta: Está pregunta es útil y esta claraNo me gusta: Está pregunta no esta clara o no es útil
0
Responder

RE:Directivas del Preprocesador

Publicado por fernando.gomez (1603 intervenciones) el 05/06/2006 22:05:40
Hola. La finalidad de las directivas de preprocesador es indicarle al compilador cómo debe compilar. Probablemente la directiva más importante es la de #include:

#include <archivo>
#include "archivo"

Según ANSI C, la primera línea del ejemplo busca el archivo dentro de los directorios de cada compilador en particular, mientras que la segunda línea, busca en términos relativos al archivo que hace el #include. Esta directiva le dice que copie el contenido del archivo al y que reemplace la frase "#include <archivo>" por el contenido del mismo.

La segunda directiva más importante -probablemente- es el #define. Con #define, defines un símbolo:

#define NULL (void*)0

Así, en lugar de tener que escribir:

int* p = (void*)0;

haces

int* p = NULL;

Un #define le dice al compilador que antes de compilar, reemplace (expanda es la palabra técnica) el símbolo por su valor. Así:

int* p = NULL;

el preprocesador lo transforma en:

int* p = (void*)0;

Una finalidad de emplear #define's es evitar la aparición de números mágicos. Semánticamente, es diferente asignarle a un puntero un NULL -que se interpreta como si el puntero apuntara a ningún lado- a asignarle un 0 -que se interpretaría como asignarle la dirección de memoria 0. En este sentido, los #define's son alternativas a las variables constantes, ya que al no ser variables, no ocupan espacio en la memoria. Por cierto, lo contrario a #define, es #undef.

Además de los #define's, existen otras tres directivas importantes: #if, #else, #elif, #endif, la primera con sus variantes: #ifdef, #ifndef. Creo que no hay pierde aquí. #ifdef X es equivalente a #if defined(X) y por otra parte, #ifndef X es equivalente a #if !defined(X). La idea es que dependiendo de ciertas condiciones de preprocesamiento, se procesen tales o cuáles porciones de código.

Como sabrás, ANSI C define el tipo de dato char, el cuál se usa en funciones como strcpy, fprintf, y sprintf. Pero también -a partir de ANSI C 99- se definió el tipo de dato wchar_t, que es un caracter de UNICODE. A cada función con char, se le hizo una equivalente que emplee wchar_t: wstrcpy, wfprintf, swprintf, etc. Algo común en el desarrollo de programas bajo Windows, es el siguiente:

#ifdef UNICODE
#define TCHAR wchar_t
#define _tcscpy wcscpy
#define _tfprintf wfprintf
#define _tsprintf swprintf
#define _T(x) Lx
#else
#define TCHAR char
#define _T(x) x
#define _tcscpy strcpy
#define _tfprintf fprintf
#define _tsprintf sprintf
#endif

Con lo anterior, yo puedo escribir mi código independientemente de si lo compilo para unicode o no:

#define TAMANO_CADENA 10
TCHAR sz[TAMANO_CADENA];

_tcscpy(sz, _T("Hola Mundo!"));

Si defino el símbolo UNICODE (i.e. le digo al compilador que lo defina, o hago manualmente el #define), el código anterior se transformaría en

wchar_t sz[10];

wcscpy(sz, L"Hola Mundo!");

que es lo correcto definido por ANSI C99. Si UNICODE no está definido, el código anterior se transforma en:

char sz[10];

strcpy(sz, "Hola Mundo!");

Como ves, los #defines, empleados en conjunto con #ifdef (et al) son una herramienta poderosa.

Finalmente, cuando empleas #define's en aras de reemplazar código directamente, se dice que empleas un MACRO. Un ejemplo de macro es el siguiente:

#define TRACE(x) \
{
std::ofstream strmFile; \
strm.open("trace.log", ios::app); \
strm << x << endl; \
strm.close(); \
}

El caracter \ le dice al preprocesador que las instrucciones continúan en la siguiente línea. Así, para hacer un rastreo en mi aplicación, sólo tengo que emplear TRACE y listo:

int main()
{
TRACE("iniciando aplicacion");

for (int i = 0; i < 10; i++) {
TRACE(i);
...
}

TRACE("saliendo de la aplicación");

return EXIT_SUCCESS;
}

Creo que he cubierto lo importante sobre preprocesamiento (me falta el operador ##, pero bueno, eso ya es más avanzado), si tienes más dudas, házmelas saber.

Saludos.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Nunca te acostaras sin saberr algo mas...

Publicado por Nelek (816 intervenciones) el 07/06/2006 08:20:23
Gracias fernando, la verdad es que vamos a tener que ponerte un monumento como sigas asi... :)
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar