Entorno y preprocesamiento del programa C

​​​​​​Directorio de artículos

1. Entorno de traducción de programas y entorno de ejecución.

1. Proceso de compilación del programa.

2. Compilar el principio interno.

3. Entorno de ejecución

 2. Preprocesamiento antes de que se ejecute el programa.

1. Inducción de símbolos predefinidos.

2.definir identificador de definición

3.definir definición de macro

4.definir reglas de reemplazo

5. Comparación de macros y funciones.

Tres, la forma en que se incluye el archivo de encabezado 

4. Ejercicio: escriba una macro que pueda intercambiar los bits de paridad de un bit binario entero


  Hola a todos, soy Ji Ning.

  En cualquier implementación de ANSI C, existen dos entornos distintos. El primero es el entorno de traducción, donde el código fuente se convierte en instrucciones de máquina ejecutables. El segundo es el entorno de ejecución, que se utiliza para ejecutar el código. También existen muchos principios internos en el entorno de la traducción, aprendamos juntos.

1. Entorno de traducción de programas y entorno de ejecución.

1. Proceso de compilación del programa.

  

  Entre ellos, el archivo de origen es el archivo con el sufijo de nombre de archivo .c, y el archivo de destino es el archivo con el sufijo de archivo .obj.

  Cada archivo fuente que compone un programa se convierte individualmente en código objeto mediante el proceso de compilación. Cada archivo objeto se agrupa mediante un vinculador para formar un programa ejecutable único y completo. El vinculador también importará cualquier función utilizada por el programa en la biblioteca de funciones estándar de C, y puede buscar en la biblioteca de programas personales del programador y vincular las funciones que necesita al programa.

2. Compilar el principio interno.

  En la etapa de preprocesamiento, el sistema procesará automáticamente todas las instrucciones precompiladas. Incluyendo la eliminación de comentarios, incluidos los archivos de encabezado, #define, definiendo símbolos y reemplazando macros, etc.

  En la etapa de compilación, el sistema traducirá el código del lenguaje C a instrucciones ensambladoras, así como análisis de sintaxis, análisis del significado de las palabras, análisis semántico, resumen de símbolos, etc.

  en la etapa de compilación. El sistema traduce las instrucciones de ensamblaje en instrucciones binarias y genera archivos de objetos .o para formar una tabla de símbolos.

  Finalmente, la tabla de segmentos se fusiona en la etapa de vinculación y la tabla de símbolos se reubica y sintetiza ( en la etapa de vinculación, se puede encontrar si la función está definida normalmente )

3. Entorno de ejecución

  Primero, el programa debe cargarse en la memoria. En un entorno con sistema operativo: Generalmente esto lo hace el sistema operativo. En un entorno independiente, la carga del programa se debe organizar manualmente, posiblemente colocando el código ejecutable en la memoria de solo lectura.

  El siguiente paso es iniciar la ejecución del programa, llamando a la función principal para comenzar a ejecutar el código del programa. En este momento, el programa utilizará una pila de tiempo de ejecución (pila) para almacenar las variables locales y la dirección de retorno de la función. Los programas también pueden utilizar memoria estática. Las variables almacenadas en la memoria estática conservan sus valores durante la ejecución del programa.

  El último es terminar el programa. La función principal finaliza normalmente o puede finalizar inesperadamente.

 2. Preprocesamiento antes de que se ejecute el programa.

1. Inducción de símbolos predefinidos.

  Los siguientes símbolos son símbolos integrados del lenguaje C.

__ARCHIVO__  archivos fuente para compilación
__LÍNEA__  el número de línea actual del archivo
__FECHA__  La fecha en que se compiló el archivo.
__TIEMPO__ La hora en que se compiló el archivo.

Los símbolos correspondientes utilizados en el programa se convierten automáticamente al significado correspondiente en la etapa de preprocesamiento. El código de prueba es el siguiente

#include<stdio.h>
int main() {
    printf("%d\n", __LINE__);
    printf("%s\n", __TIME__);
    printf("%s\n", __DATE__);
    printf("%s\n", __FILE__);
    return 0;
}

resultado de la operación

  Dado que _LINE_ está en la cuarta línea, el resultado de la conversión es 4; la hora y la fecha son la hora y la fecha en que probé el código; _FILE_ corresponde a la ruta de mi archivo fuente.

2.definir identificador de definición

La sintaxis es la siguiente:

#define name stuff

  En la etapa de preprocesamiento, el compilador reemplazará automáticamente todos los nombres en el código con cosas. Sin embargo, cabe señalar que al escribir #define para definir un identificador, no puede agregar ; después, y el compilador usará ; en la etapa de preprocesamiento como contenido a reemplazar.

3.definir definición de macro

  El mecanismo #define incluye una disposición que permite sustituir parámetros en el texto, y esta implementación a menudo se denomina macro o
macro de definición. Así es como se declara la macro:

#define name( parament-list ) stuff

  La lista de parámetros es una lista de símbolos separados por comas que pueden aparecer en cosas. Su método de uso es similar al de una función, excepto que el parámetro de la función se cambia a un reemplazo directo. Debido a que se ha cambiado a un reemplazo directo, los parámetros de la macro deben modificarse entre paréntesis tanto como sea posible . En segundo lugar, el paréntesis de apertura de la lista de parámetros debe estar inmediatamente adyacente al nombre, porque si hay algún espacio en blanco entre los dos, la lista de parámetros se interpretará como parte del material.

  Los parámetros macro generalmente se reemplazan muchas veces, por lo que aquellos parámetros con "efectos secundarios" no se pueden usar. Los efectos secundarios son efectos permanentes en la evaluación de expresiones, como las operaciones de autosuma y autoresta.

  Por ejemplo, busque el resultado de la operación del siguiente código:

​#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);

  La mayoría de las personas pueden dar por sentado que lo que se pasa es el valor de las expresiones x++ e y++, entonces la estructura debería ser el resultado de z debería ser 8, el resultado de x debería ser 6 y el resultado de y debería ser 9. Pero no lo es:

  Este es el efecto de los parámetros con efectos secundarios. 

4.definir reglas de reemplazo

Hay varios pasos a seguir al expandir #defines para definir símbolos y macros en un programa.

1. Al llamar a una macro, primero se verifican los parámetros para ver si contienen algún símbolo definido por #define. En caso afirmativo, se reemplazan primero.

2. A continuación, el texto de sustitución se inserta en el programa en lugar del texto original. Para las macros, los nombres de los parámetros se reemplazan por sus valores.

3. Finalmente, el archivo resultante se escanea nuevamente para ver si contiene algún símbolo definido por #define. En caso afirmativo, repita el procesamiento anterior.

Hay algunos puntos más a tener en cuenta.

1. Los símbolos definidos por otros #defines pueden aparecer en parámetros macro y definiciones #define. Pero con las macros, la recursividad no puede ocurrir .

2. Cuando el preprocesador busca símbolos definidos por #define, no se busca el contenido de la constante de cadena

5. Comparación de macros y funciones.

  En algunos aspectos, las macros son muy ventajosas. Por ejemplo, una tarea simple como encontrar el valor máximo de dos números puede llevar mucho tiempo si se realiza con una función, porque cada vez que se llama a una función, se debe crear y destruir el marco de pila de funciones, pero la macro no lo necesita; Los parámetros deben ser de un tipo específico, pero los parámetros macro pueden pasar múltiples tipos de parámetros dentro del alcance de las operaciones aritméticas. El resumen es: en proyectos pequeños, las macros son mejores que las funciones en términos de tamaño y velocidad del programa ; las macros son independientes del tipo y los tipos se pueden pasar como parámetros, algo que las funciones no pueden hacer .

  Pero las macros también tienen las siguientes desventajas:

1. Cada vez que se utiliza una macro, se insertará una copia del código de definición de la macro en el programa. A menos que la macro sea relativamente corta, puede aumentar considerablemente la duración del programa.

2. Las macros no se pueden depurar.

3. Dado que las macros son independientes del tipo, no son lo suficientemente rigurosas.

4. Las macros pueden causar problemas de prioridad del operador, lo que hace que la programación sea propensa a errores. 

Tres, la forma en que se incluye el archivo de encabezado 

  En lenguaje C, hay dos formas de incluir archivos de encabezado: " "incluir e < >incluir.

  " "La inclusión es una forma que tiene el compilador de incluir archivos locales. La estrategia de búsqueda es buscar primero en el directorio donde se encuentra el archivo fuente. Si no se encuentra el archivo de encabezado, el compilador buscará el archivo de encabezado en el estándar ubicación es como buscar el archivo de encabezado de la función de la biblioteca. Si se encuentra, de lo contrario, generará un error de compilación .

  El archivo de encabezado de búsqueda contenido en <> se busca directamente en la ruta estándar y, si no se puede encontrar, generará un error de compilación

  Después de ver esta situación, algunas personas pueden preguntar: ¿Se pueden incluir todos los archivos con " " en el futuro? La respuesta es sí, pero si considera la eficiencia del programa, es otra cuestión, porque " " para verificar el archivo de encabezado. de la ruta estándar debe buscarse dos veces, pero el archivo de encabezado de la búsqueda <> de la ruta estándar solo debe buscarse una vez, y la eficiencia es obviamente mucho mayor .

  Para evitar que se haga referencia repetida a los archivos de encabezado, habrá contenido compilado condicionalmente en los archivos de encabezado para evitar la duplicación del contenido del archivo.

Escriba estas pocas declaraciones al comienzo de cada archivo de encabezado.

#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif  //__TEST_H_

o:

#pragma once

Puede evitar referencias repetidas a archivos de encabezado.

A continuación se muestran algunas directivas de compilación condicional de uso común.

#si
#elif
#terminara si

4. Ejercicio: escriba una macro que pueda intercambiar los bits de paridad de un bit binario entero

  Idea: saca el dígito impar del dígito binario de este número y muévelo un bit hacia la izquierda; saca el dígito par del dígito binario de este número, muévelo un bit hacia la derecha y finalmente súmalo.

  Tome el binario de 8 bits como ejemplo: &0x55 puede obtener su bit impar, &0xaa puede obtener su bit par, si aumenta a binario de 32 bits, entonces &0x55555555 puede obtener su bit impar y luego desplazarse hacia la izquierda, &0xaaaaaaaa puede obtener su bit par bit, luego desplazamiento a la derecha y finalmente suma para intercambiar los bits de paridad de los bits binarios de este número.

  Si comprende el diagrama, es muy sencillo escribir código e implementarlo con macros.

#include<stdio.h>
#define SWAP(n)  (n=((n&0xaaaaaaaa)>>1)+((n&0x55555555)<<1))
int main()
{
	int n = -120;
	SWAP(n);//交换奇偶位
	printf("%d\n", n);
	SWAP(n);//再交换回来
	printf("%d\n", n);
	return 0;
}

Utilice -120 para verificar que el intercambio de paridad fue exitoso

resultado de la operación:

  El tiempo vuela y, en un abrir y cerrar de ojos, el blog en lenguaje C casi termina y más adelante se lanzarán algunas soluciones de entrevistas clásicas en lenguaje C, espérelo con ansias. 

  En el próximo blog, comenzaré a aprender y actualizar el contenido de la estructura de datos y el algoritmo. Gracias por su apoyo a Ji Ning.

Supongo que te gusta

Origin blog.csdn.net/zyb___/article/details/131870314
Recomendado
Clasificación