El preprocesador (parte 1)

En el primer episodio de la segunda temporada del Tutorial de C comenzamos a hablar del preprocesador, hasta ahora tratada como una deidad. ¿Cómo funciona realmente el preprocesador? Hablaremos de las directivas soportadas según el estándar de C.

Cuando compilamos un programa en C, detrás de escena ocurre un proceso que a menudo pasa desapercibido pero que es fundamental para que nuestro código funcione correctamente: la acción del preprocesador. Este programa se encarga de preparar nuestro código antes de que el compilador principal lo transforme en un ejecutable, y entender cómo funciona nos permite aprovechar mejor las herramientas que nos ofrece el lenguaje.

El preprocesador actúa leyendo nuestro archivo fuente línea a línea y realizando ciertas transformaciones. Por ejemplo, elimina comentarios y espacios innecesarios para facilitar el trabajo del compilador. Pero su función más visible y conocida es la gestión de las directivas que comienzan con una almohadilla #, como include, define y undef.

La directiva include es la que usamos para importar archivos, generalmente archivos de cabecera que contienen declaraciones y definiciones necesarias para nuestro programa. Cuando escribimos algo como

#include <stdio.h>

lo que realmente hace el preprocesador es buscar el archivo stdio.h en una ubicación estándar del sistema, como /usr/include en sistemas Unix, y copiar todo su contenido en el punto donde aparece la directiva. Esto es equivalente a pegar literalmente ese código en nuestro archivo fuente. Si usamos comillas en lugar de los símbolos de menor y mayor que, por ejemplo

#include "miarchivo.h"

el preprocesador buscará el archivo en el directorio donde está nuestro archivo fuente, lo que nos permite incluir archivos propios de forma relativa.

Otra directiva fundamental es define, que nos permite crear sustituciones de texto. No se trata de declarar variables, sino de indicarle al preprocesador que cada vez que encuentre un identificador determinado, lo reemplace por otro texto que le indiquemos. Por ejemplo, si escribimos

#define LIMITE 100

cada vez que aparezca LIMITE en el código, el preprocesador lo sustituirá por 100. Esto puede usarse para definir constantes o incluso para hacer sustituciones más creativas. Por ejemplo, si definimos

#define principal main

podemos escribir una función llamada principal y el preprocesador la convertirá en main antes de la compilación, haciendo que el programa funcione correctamente a pesar de que el nombre original no sea el esperado por el compilador.

Es importante destacar que estas sustituciones solo afectan al código que aparece después de la directiva define. Por eso, aunque técnicamente podemos colocar un define en cualquier parte del archivo, lo habitual y recomendable es ponerlos al principio para que su efecto sea claro y consistente.

Además, el preprocesador nos permite eliminar estas definiciones con la directiva undef. Si hacemos

#undef LIMITE

el preprocesador olvidará la sustitución para LIMITE a partir de ese punto, y cualquier aparición posterior de LIMITE no será reemplazada. Esto nos da flexibilidad para cambiar el significado de identificadores durante la compilación, aunque hay que usarlo con cuidado para no generar confusión.

También podemos usar define sin especificar un valor de sustitución, simplemente para registrar que un identificador está definido. Esto es útil para condicionales en el preprocesador, como #ifdef o #ifndef, que permiten incluir o excluir partes del código según si una macro está definida o no. Pero este tema merece una explicación aparte.

En definitiva, el preprocesador es una herramienta poderosa que transforma nuestro código antes de que llegue al compilador, permitiéndonos incluir archivos, definir constantes y realizar sustituciones que pueden facilitar o complicar nuestro trabajo según cómo las usemos. Comprender su funcionamiento nos abre la puerta a escribir programas en C más estructurados y flexibles.

Lista de reproducción
  1. 1
    Instalar CodeBlocks
    15 minutos
  2. 2
    Funciones y hola mundo
    17 minutos
  3. 3
    Variables y tipos de datos
    17 minutos
  4. 4
    Condicionales y operadores lógicos
    16 minutos
  5. 5
    Bucles
    11 minutos
  6. 6
    Punteros
    12 minutos
  7. 7
    Arrays
    14 minutos
  8. 8
    Estructuras
    12 minutos
  9. 9
    Otras construcciones de C
    9 minutos
  10. 10
    Memoria dinámica
    8 minutos
  11. 11
    El preprocesador (parte 1)
    10 minutos
  12. 12
    El preprocesador (parte 2)
    9 minutos
  13. 13
    Archivos de cabecera y múltiples .c (parte 1)
    8 minutos
  14. 14
    Archivos de cabecera y múltiples .c (parte 2)
    9 minutos
  15. 15
    C desde la línea de comandos (parte 1)
    8 minutos
  16. 16
    C desde la línea de comandos (parte 2)
    9 minutos
  17. 17
    Break y continue
    10 minutos
  18. 18
    Goto
    13 minutos
  19. 19
    Manipulación de bits
    15 minutos
  20. 20
    Máscaras de bit
    19 minutos
  21. 21
    Archivos (1): fopen y fclose
    13 minutos
  22. 22
    Archivos (2): leer con fgetc
    9 minutos
  23. 23
    Archivos (3): fseek y ftell
    11 minutos
  24. 24
    Archivos (4): leer con fgets
    9 minutos
  25. 25
    Archivos (5): fputc y fputs
    7 minutos
  26. 26
    Archivos (6): volcar en archivos con fwrite
    10 minutos
  27. 27
    Archivos (7): fread, fwrite y los arrays
    10 minutos
  28. 28
    Archivos (8): entrada estándar y salida estándar
    9 minutos
  29. 29
    Archivos (9): buffers
    14 minutos
  30. 30
    Archivos (y 10): otras funciones útiles con archivos
    5 minutos
  31. 31
    printf (1)
    18 minutos
  32. 32
    printf (parte 2)
    12 minutos
  33. 33
    scanf (parte 1)
    17 minutos
  34. 34
    scanf (parte 2)
    17 minutos
  35. 35
    fprintf, sprintf y snprintf
    8 minutos
  36. 36
    Tipos de datos opacos
    13 minutos
  37. 37
    Bibliotecas estáticas
    13 minutos
  38. 38
    Bibliotecas dinámicas
    15 minutos
  39. 39
    Más flags: i mayúscula (include), wall, werror, pedantic...
    12 minutos
  40. 40
    pkg-config
    12 minutos
  41. 41
    Make
    17 minutos
  42. 42
    GDB
    21 minutos
  43. 43
    Variables globales
    6 minutos
  44. 44
    extern
    9 minutos
  45. 45
    Funciones variádicas
    12 minutos
  46. 46
    El optimizador de GCC y la opción -O
    12 minutos
  47. 47
    Volatile
    6 minutos