Directivas del Preprocesador
## Objetivos de Aprendizaje
- Comprender el preprocesador de C
- Dominar directivas de inclusión
- Trabajar con macros
- Usar compilación condicional
## Descripción General del Preprocesador
El preprocesador se ejecuta antes de la compilación, transformando el código fuente.
```c
#define PI 3.14159
```
Se convierte en:
```c
3.14159
```
## Directivas de Inclusión
### #include
```c
#include // Encabezado del sistema
#include "myheader.h" // Encabezado del usuario
```
### Guardas de Inclusión
```c
// myheader.h
#ifndef MYHEADER_H
#define MYHEADER_H
// Contenido del encabezado
#endif
```
### #pragma once (Moderno)
```c
// myheader.h
#pragma once
// Contenido del encabezado
```
## Definición de Macros
### Macros Tipo Objeto
```c
#define PI 3.14159
#define MAX_SIZE 100
#define TRUE 1
#define FALSE 0
```
### Macros Tipo Función
```c
#define SQUARE(x) ((x) * (x))
int result = SQUARE(5); // 25
```
### Múltiples Argumentos
```c
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int m = MAX(10, 20); // 20
```
### ¿Por Qué Usar Paréntesis?
```c
// Sin paréntesis
#define SQUARE(x) x * x
int result = SQUARE(5); // 25 (correcto)
int result = SQUARE(2 + 3); // 2 + 3 * 2 + 3 = 11 (¡INCORRECTO!)
// Con paréntesis
#define SQUARE(x) ((x) * (x))
int result = SQUARE(2 + 3); // ((2 + 3) * (2 + 3)) = 25 (correcto)
```
## Concatenación de Tokens
### Operador de Concatenación de Tokens
```c
#define CONCAT(a, b) a ## b
int xy = 10;
CONCAT(x, y); // Se expande a xy
```
## Stringificación
### Operador de Stringificación
```c
#define PRINT_INT(x) printf(#x " = %d\n", x)
int value = 42;
PRINT_INT(value);
// Se expande a:
// printf("value" " = %d\n", value);
```
## Compilación Condicional
### #ifdef / #ifndef
```c
#define DEBUG 1
#ifdef DEBUG
printf("Debug mode\n");
#endif
#ifndef PI
#define PI 3.14159
#endif
```
### #if / #else / #elif
```c
#if __STDC_VERSION__ >= 201112L
// C11 o posterior
printf("C11 or later\n");
#elif __STDC_VERSION__ >= 199901L
// C99
printf("C99\n");
#else
// C89/C90
printf("C89/C90\n");
#endif
```
### Operador Defined
```c
#if defined(DEBUG) && DEBUG > 0
printf("Debug level: %d\n", DEBUG);
#endif
// Forma corta:
#if defined(DEBUG)
// ...
#endif
// Forma aún más corta:
#ifdef DEBUG
// ...
#endif
```
## Macros Predefinidas
| Macro | Descripción |
|-------|-------------|
| `__FILE__` | Nombre del archivo actual |
| `__LINE__` | Número de línea actual |
| `__DATE__` | Fecha de compilación |
| `__TIME__` | Hora de compilación |
| `__STDC__` | Conformidad con ISO C |
| `__STDC_VERSION__` | Versión del estándar C |
| `__func__` | Nombre de la función actual |
```c
#include
int main(void) {
printf("File: %s\n", __FILE__);
printf("Line: %d\n", __LINE__);
printf("Date: %s\n", __DATE__);
printf("Time: %s\n", __TIME__);
printf("Function: %s\n", __func__);
return 0;
}
```
## Macro Assert
```c
#include
int divide(int a, int b) {
assert(b != 0); // Falla si b es 0
return a / b;
}
```
### Desactivar Aserciones
```c
#define NDEBUG // Desactivar aserciones
#include
```
## Macros Variádicos
```c
#define DEBUG(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
DEBUG("Error %d: %s\n", errno, strerror(errno));
```
### Argumento Variádico Vacío
```c
#define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__)
LOG("Hello\n"); // Funciona
LOG("Value: %d\n", 42); // Funciona
```
## Eliminar Macros
```c
#define PI 3.14159
printf("%f\n", PI);
#undef PI
#define PI 3.14 // Ahora se permite redefinir
```
## Operadores del Preprocesador
### #pragma
```c
#pragma pack(push, 1)
struct Packed {
char c;
int x;
};
#pragma pack(pop)
```
### _Pragma
```c
_Pragma("pack(push, 1)")
struct Packed {
char c;
int x;
};
_Pragma("pack(pop)")
```
## Patrones Comunes
### Guardas de Encabezado
```c
// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
int add(int a, int b);
int subtract(int a, int b);
#endif
```
### Detección de Plataforma
```c
#ifdef _WIN32
// Windows
#include
#elif __linux__
// Linux
#include
#elif __APPLE__
// macOS
#include
#endif
```
### Prueba de Características
```c
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L
// Usar funciones POSIX
#endif
```
### Registro de Depuración
```c
#define DEBUG
#ifdef DEBUG
#define LOG(fmt, ...) fprintf(stderr, "[%s:%d] " fmt "\n", \
__FILE__, __LINE__, ##__VA_ARGS__)
#else
#define LOG(fmt, ...) // Vacío en producción
#endif
```
### Obsolescencia
```c
#define DEPRECATED __attribute__((deprecated))
DEPRECATED
void oldFunction(void) {
// ...
}
```
## Macro vs Función
| Aspecto | Macro | Función |
|---------|-------|----------|
| Velocidad | Más rápida (sin sobrecarga de llamada) | Más lenta |
| Tipo | Genérica (sustitución de texto) | Tipo-seguro |
| Depuración | Más difícil | Más fácil |
| Argumentos | Evaluados en cada uso | Evaluados una vez |
## Resumen
- El preprocesador se ejecuta antes de la compilación
- `#include` incluye archivos de encabezado
- Usar guardas de inclusión o `#pragma once`
- `#define` crea macros
- Las macros de función deben usar paréntesis
- `#ifdef`/`#if` para compilación condicional
- Macros predefinidas: `__FILE__`, `__LINE__`, `__DATE__`
- `NDEBUG` desactiva aserciones
- Macros variádicos: `__VA_ARGS__`
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →