RE:Directivas del Preprocesador
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.