C Preprocesador

Preprocesador de C (CPP)Es una etapa independiente en el proceso de compilación, antes dela compilación realRealiza el procesamiento de texto del código fuente. Las funciones principales incluyen:

  • Expansión de macros
  • Inclusión de archivos
  • Compilación condicional
  • Procesamiento de directivas especiales

El preprocesador de CNo es parte del compilador, pero es un paso separado en el proceso de compilación.

En resumen, el preprocesador de C no es más que una herramienta de reemplazo de texto; indica al compilador que realice el preprocesamiento necesario antes de la compilación real.

Abreviaremos el preprocesador de C (C Preprocessor) como CPP.

Todos los comandos del preprocesador comienzan con el símbolo de almohadilla#al principio. Debe ser el primer carácter que no sea un espacio en blanco. Para mejorar la legibilidad, las directivas del preprocesador deben comenzar en la primera columna.

A continuación se enumeran todas las directivas importantes del preprocesador:

Directiva Descripción Ejemplo de uso
#define Define una macro (constante simbólica o macro de función) #define PI 3.14159
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#include Incluye un archivo de cabecera #include <stdio.h>
#include "myheader.h"
#undef Cancela la definición de una macro #undef PI
#ifdef Compila el código siguiente si la macro está definida #ifdef DEBUG
printf("Debug info\n");
#endif
#ifndef Compila el código siguiente si la macro no está definida (se usa a menudo para la protección de archivos de cabecera) #ifndef HEADER_H
#define HEADER_H
/* contenido */
#endif
#if Compilación condicional (se puede usar con el operador defined) #if VERSION > 2
/* código de la nueva versión */
#endif
#else #if/#ifdef/#ifndefRama alternativa #ifdef WIN32
/* Windows代码 */
#else
/* otros sistemas */
#endif
#elif Similar a else if #if defined(UNIX)
/* Unix代码 */
#elif defined(WIN32)
/* Windows代码 */
#endif
#endif Finaliza el bloque de compilación condicional Como se muestra en el ejemplo anterior
#error Genera un error de compilación y muestra un mensaje #if !defined(C99)
#error "需要C99标准"
#endif
#pragma Directivas específicas del compilador (no estándar, varían según el compilador) #pragma once
#pragma pack(1)

Ejemplo de preprocesador

Analiza el siguiente ejemplo para comprender las diferentes directivas.

#define MAX_ARRAY_LENGTH 20

Esta directiva le indica a CPP que defina todas las MAX_ARRAY_LENGTH como 20. Use#definela definición de constantes para mejorar la legibilidad.

#include <stdio.h>
#include "myheader.h"

Estas directivas le indican a CPP que obtenga dela biblioteca del sistemaobtenga stdio.h, y agregue el texto al archivo fuente actual. La siguiente línea le indica a CPP que obtenga del directorio localmyheader.h, y agregue su contenido al archivo fuente actual.

#undef  FILE_SIZE
#define FILE_SIZE 42

Esta directiva le indica a CPP que cancele la definición de FILE_SIZE y lo defina como 42.

#ifndef MESSAGE
   #define MESSAGE "You wish!"
#endif

Esta directiva le indica a CPP que defina MESSAGE solo si MESSAGE no está definido.

#ifdef DEBUG
   /* Your debugging statements here */
#endif

Esta directiva le indica a CPP que, si DEBUG está definido, ejecute las declaraciones de procesamiento. Al compilar, si le pasa al compilador gcc-DDEBUGel indicador, esta directiva es muy útil. Define DEBUG y usted puede activar o desactivar la depuración en cualquier momento durante la compilación.

Ejemplo

#include <stdio.h>

// Definir una macro constante
#define PI 3.1415926
#define GREETING "Hello, World!"

// Definir una macro de función (presta atención al uso de paréntesis)
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))

// Ejemplo de compilación condicional
#define DEBUG 1

int main() {
    // Usar la macro constante
    printf("El valor de PI: %f\n", PI);
    printf("%s\n", GREETING);
   
    "El cuadrado de %d es: %d
    int x = 5;
    printf("El mayor de 3 y 5 es: %d\n", x, SQUARE(x));
    printf(// Ejemplo de compilación condicional\n", MAX(3, 5));
   
    "[Información de depuración] El programa llegó a la función main
    #ifdef DEBUG
    printf(// Verificación de la versión del compilador\n");
    #endif
   
    "Usando el estándar C11
    #if __STDC_VERSION__ >= 201112L
    printf("Usando el estándar C99\n");
    #elif __STDC_VERSION__ >= 199901L
    printf("Usando el estándar C89/C90\n");
    #else
    printf(// Ejemplo de directiva de error (descomentar provocará un error de compilación)\n");
    #endif
   
    // #error "Este es un error activado manualmente"
    Recomendaciones de mejores prácticas
   
    return 0;
}

Recomendaciones de mejores prácticas

  1. Usar letras mayúsculas y guiones bajos para nombrar macros:

    • Ejemplo:
    • Consideraciones sobre las macros de función#define MAX_SIZE 100
  2. Cada parámetro y la expresión completa deben ir entre paréntesis:

    • Evite usar argumentos con efectos secundarios (por ejemplo,
    • Protección de archivos de cabeceraSQUARE(x++))
  3. Depuración con compilación condicional:

    #ifndef MY_HEADER_H
    #define MY_HEADER_H
    /* 头文件内容 */
    #endif
  4. Desarrollo multiplataforma:

    #ifdef DEBUG
    #define DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
    #else
    #define DEBUG_PRINT(fmt, ...)
    #endif
    
  5. Macros predefinidas:

    #if defined(_WIN32)
    // Windows特定代码
    #elif defined(__linux__)
    // Linux特定代码
    #elif defined(__APPLE__)
    // macOS特定代码
    #endif
    

Macros predefinidas

Descripción

macroFecha actual, una constante de caracteres en el formato "MMM DD YYYY".
__DATE__Hora actual, una constante de caracteres en el formato "HH:MM:SS".
__TIME__Esto contiene el nombre del archivo actual, una constante de cadena.
__FILE__Esto contiene el número de línea actual, una constante decimal.
__LINE__Se define como 1 cuando el compilador compila con el estándar ANSI.
__STDC__Probemos el siguiente ejemplo:

Ejemplo

Ejemplo

#include <stdio.h> /*// Imprimir el nombre del archivo fuente actual (constante de cadena)*/ int main() { Archivo actual: %s printf("// Imprimir la fecha de compilación (formato "MMM DD YYYY")\n", __FILE__); Fecha de compilación: %s printf("// Imprimir la hora de compilación (formato "HH:MM:SS")\n", __DATE__); Hora de compilación: %s printf("// Imprimir el número de línea actual (entero decimal)\n", __TIME__); Número de línea actual: %d printf("// Verificar si cumple con el estándar ANSI/ISO (1 indica que cumple)\n", __LINE__); Estándar ANSI: %d printf("// Ejemplo práctico: salida de información de depuración\n", __STDC__); n[Información de depuración] %s (línea %d) compilado en %s %s printf("\Cuando el código anterior (en el archivo\n", __FILE__, __LINE__, __DATE__, __TIME__); return 0; }

) se compila y se ejecuta, produce el siguiente resultado:test.cOperadores del preprocesador

当前文件: predef_macros.c
编译日期: Jul 5 2023
编译时间: 14:30:45
当前行号: 13
ANSI标准: 1

[调试信息] predef_macros.c (第16行) 编译于 Jul 5 2023 14:30:45

Operadores del preprocesador

Operador de continuación de macro (\)

Operador de continuación de macro (\)

Operador de conversión a cadena (#)

#define  message_for(a, b)  \
    printf(#a " and " #b ": We love you!\n")

En la definición de una macro, cuando es necesario convertir un parámetro de la macro en una constante de cadena, se usa el operador de conversión a cadena (#). Este operador, usado en una macro, tiene un parámetro específico o una lista de parámetros. Por ejemplo:

Ejemplo

Ejemplo

#include <stdio.h> #define message_for(a, b) \ printf(#a " and " #b ": We love you!\n") int main(void) { message_for(Carole, Debra); return 0; }

Operador de pegado de tokens (##)

Carole and Debra: We love you!

Operador de pegado de tokens (##)

Ejemplo

Ejemplo

#include <stdio.h> #define tokenpaster(n) printf ("token" #n " = %d", token##n) int main(void) { int token34 = 40; tokenpaster(34); return 0; }

Esto sucede porque este ejemplo produce la siguiente salida real del compilador:

token34 = 40

Este ejemplo demuestra que token##n se concatena para formar token34, donde hemos usado

printf ("token34 = %d", token34);

el operador de conversión a cadena (#)y el operador de pegado de tokens (##)yOperador defined()。

Operador defined()

se usa en expresiones constantes, para determinar si un identificador ya ha sido definido con #define. Si el identificador especificado está definido, el valor es verdadero (distinto de cero). Si el identificador especificado no está definido, el valor es falso (cero). El siguiente ejemplo demuestra el uso del operador defined():definedEjemplo

Ejemplo

#include <stdio.h> #if !defined (MESSAGE) #define MESSAGE "You wish!" #endif int main(void) { printf("Here is the message: %s\n", MESSAGE); return 0; }

Macros parametrizadas

Here is the message: You wish!

Macros parametrizadas

Podemos reescribir el código anterior usando una macro, de la siguiente manera:

int square(int x) {
   return x * x;
}

Antes de usar una macro con parámetros, debe usar

#define square(x) ((x) * (x))

la directiva #define para definirla. La lista de parámetros se encierra entre paréntesis y debe ir inmediatamente después del nombre de la macro. No se permite ningún espacio entre el nombre de la macro y el paréntesis izquierdo. Por ejemplo:#defineEjemplo

Ejemplo

#include <stdio.h> #define MAX(x,y) ((x) > (y) ? (x) : (y)) int main(void) { printf("Max between 20 and 10 is %d\n", MAX(10, 20)); return 0; }

Otras extensiones

Max between 20 and 10 is 20
AI pensando...