4.8 [Lenguaje C incorporado: algunos extraviados pero dignos de discusión]

4.8.1 ¿Qué es el sistema operativo?

4.8.1.1 ¿Qué es el sistema operativo?
操作系统之于计算机,犹豫行政系统之于国家;诞生于“国”,服务于“国”。

Con el desarrollo de la tecnología informática, el rendimiento y los recursos de la computadora han aumentado considerablemente. En este momento, la escritura de códigos también debe producir clases y división del trabajo. De lo contrario, si todos los códigos participan en el trabajo directo, la eficiencia general del sistema no es alta. (Porque el código es difícil de optimizar la asignación de recursos). La solución es el sistema operativo. El sistema operativo es una clase de gestión separada. El código del sistema operativo en sí mismo no genera valor directamente. Su tarea principal es administrar todos los recursos. Principalmente proporciona servicios para aquellos programas (varias aplicaciones) que generan directamente valor y mano de obra directa. Entonces, el sistema operativo es tanto un administrador como un servidor. Esto es como el proceso de formación de la sociedad humana: al principio todos trabajaban, la productividad social era baja, no había una división especial del trabajo, y cada uno se cuidaba. Más tarde, aparecieron los "micronúcleos", es decir, las "clases de gestión primitivas" como los jefes o jefes de aldea se utilizaron para servir al pequeño "sistema social" de "tribus / aldeas". Con el desarrollo y la mejora de la "sociedad", con el fin de servir mejor a esta nueva "forma social", el "sistema de gestión" se ha vuelto gradualmente más profesional, más complejo y más eficiente. "Microkernel" es para "sistema operativo" - esto es como "forma social primitiva" para nuestra "forma social moderna".

那么,问题来了,为什么会需要系统呢?干嘛不直接用裸机更方便?

Depende esencialmente de la complejidad del producto en sí. Solo los productos con funciones muy simples y CPU muy simples (como microcomputadoras de un solo chip) se desarrollarán con bare metal; los productos complejos generales se desarrollarán basados ​​en sistemas operativos.

4.8.1.2. El canal de llamada del sistema operativo: función API
(1)什么是API?它打哪来?如何产生作用?

El sistema operativo es responsable de la administración y la asignación de recursos, y el programa de aplicación es responsable de la mano de obra directa específica. La interfaz entre ellos es la función API. Cuando una aplicación necesita usar recursos del sistema (como la memoria, como la CPU, como la operación del hardware), envía una aplicación al sistema operativo a través de la API, y luego el sistema operativo responde a la aplicación para ayudar a la aplicación a realizar funciones.

4.8.1.3 ¿La relación entre las funciones de la biblioteca C y la API?

(1) La API pura solo proporciona funciones de servicio extremadamente simples sin ningún encapsulado. Estas aplicaciones de funciones están disponibles pero no son muy fáciles de usar. Para que la aplicación sea fácil de usar, soloEsta API se encapsuló por segunda vez para facilitar su uso, por lo que se convirtió en una función de biblioteca C.
(2) A veces, para completar una función, hay funciones de biblioteca correspondientes que se pueden completar, o se puede completar API, lo que sea correcto. Por ejemplo, para leer y escribir archivos, la interfaz API está abierta, escribe, lee, cierra; la interfaz de función de biblioteca es fopen fwrite fread fclose. Fopen se implementa esencialmente usando open, pero encapsulado.La encapsulación debe tener un propósito (agregar mecanismo de búfer)

4.8.1.4 ¿Cuáles son las diferencias en las funciones de la biblioteca en diferentes plataformas (windows, linux, bare metal)?

(1) Los diferentes sistemas operativos tienen diferentes API, pero todas las tareas se pueden completar, pero las API que se utilizan para completar una tarea son diferentes.
(2)Las funciones de la biblioteca también son diferentes en diferentes sistemas operativos, pero la similitud es mayor.Esto es artificial, porque las personas inconscientemente quieren proteger las diferencias entre los diferentes sistemas operativos, por lo que al encapsular API en funciones de biblioteca, intente usar el mismo conjunto de interfaces, de modo que las funciones de biblioteca encapsuladas sean bastante similares. Pero aún existen diferencias, por lo que las aplicaciones escritas en un sistema operativo no se pueden compilar y ejecutar directamente en otro sistema operativo. Entonces hay una portabilidad.
(3) Plataforma portátil entre sistemas operativos, como QT, como el lenguaje Java.

4.8.1.5 La importancia del sistema operativo: ¿la división del sistema de software?

(1) Después de tener un sistema operativo, generalmente la fabricación de un producto se puede dividir en dos partes: algunas personas son responsables de hacer el sistema operativo (impulsado por el desarrollo); algunas personas son responsables de usar el sistema operativo para implementar funciones específicas (desarrollo y aplicación). De hecho, las funciones de la capa de aplicación superior son más complicadas y se dividen en múltiples capas.

4.8.2 ¿A quién vuelve la función principal?

4.8.2.1 ¿Por qué una función necesita devolver un valor?

(1)El parámetro y el valor de retorno se diseñan cuando se diseña la función. El parámetro es la entrada de la función y el valor de retorno es la salida de la función.
(2)Debido a que la función necesita generar datos de forma externa (en realidad, algunos valores de resultado de la función en ejecución), debe devolver un valor
(3)Hablando formalmente, una función es llamada por otra función y el valor de retorno se devuelve como un valor funcional al lugar que llamó a la función.
Resumen: el valor de retorno de una función es devolver un valor a la persona que la llamó. ¿Por qué se ejecuta un determinado módulo de función B ? La razón es que el programa que diseñamos requiere el valor de salida del módulo B específico después de su ejecución. Invocar el módulo B es dar uno o más valores de retorno generados por el bloque del módulo B a la función A que llama al módulo .

4.8.2.2 ¿Quién llama a la función principal?

(1) La función principal es especial. En primer lugar, el nombre es especial. porqueEl lenguaje C estipula que la función principal es la entrada de todo el programa. Otras funciones solo pueden ejecutarse si son llamadas directa o indirectamente por la función principal, si no son llamadas directa o indirectamente por main, entonces esta función es inútil en todo el programa.
(2) Desde cierta perspectiva, la función principal representa mi programa actual o el programa completo. El comienzo de la función principal significa que todo el programa comienza a ejecutarse y el final de la función principal significa el final de todo el programa.
(3) Quien ejecuta este programa llama a main.
(4) ¿Quién ejecutó el procedimiento? En otras palabras, ¿qué tipo de métodos se llaman para la ejecución del programa?

#include <stdio.h>
/*
int main(void)
int main(int argc, char **argv)
int main(int argc, char *argv[])         //以上其实算两种main函数的标准写法
*/
/*
int sum(int a, int b)
{
    return a+b;
}

void main(void)     //这个主函数的形式在某种程度上是不合法的,原因在于主函数不可能存在任意输出形式
{
    int a = 0, b = 3, c = 0;
    a = sum(b, c);
    printf("a = %d.\n", a);
}
*/
int main(void)
{
    return 123;
}
4.8.2.3, la esencia de la ejecución de un nuevo programa bajo linux

(1) En la superficie, en Linux, vaya a la línea de comandos para ejecutar un programa ejecutable en ./xx. La ejecución de un nuevo programa en Linux es esencialmente la creación, carga, ejecución y muerte de un proceso.
(2) También podemos llamar y ejecutar un programa a través de un script de shell
(3) También podemos llamar y ejecutar un programa en el programa (fork exec)
Resumen: Podemos ejecutar un programa de muchas formas, pero son esencialmente las mismas de. La ejecución de un nuevo programa en Linux es esencialmente la creación, carga, ejecución y muerte de un proceso. Ejecutar un programa en Linux es en realidad crear un nuevo proceso y luego lanzar este programa a este proceso para ejecutarlo hasta el final. ¿Quién inició el nuevo proceso? En Linux, el proceso lo realiza su proceso padre.
Análisis: La línea de comandos en sí es un proceso. Vaya debajo de la línea de comandos. / Xx ejecuta un programa. De hecho, este nuevo programa se ejecuta como un subproceso del proceso de la línea de comandos.
En una palabra: un programa es llamado por su proceso padre.
Conclusión: La función principal vuelve al proceso padre que llamó a esta función. ¿Por qué el proceso padre quiere este valor de retorno? El proceso padre llama al proceso hijo para realizar una tarea, y luego el proceso hijo devuelve una respuesta al proceso padre a través del valor de retorno de la función principal después de la ejecución. Esta respuesta generalmente indica si el resultado de la ejecución de la tarea del proceso hijo se completó o es incorrecto. (0 significa ejecución exitosa, número negativo significa falla)

4.8.2.4, práctica de verificación para obtener el valor de retorno de la

(1) Utilice un script de shell para ejecutar el programa para obtener el valor de retorno del programa e imprimirlo.
(2) El símbolo $? Se usa en el shell de Linux para almacenar y representar el resultado de la ejecución del programa anterior.

4.8.2.5 Iluminación
(1) Todos y todo nace de una madre, y no aparecerá ni morirá sin motivo alguno.
(2) Si parece inútil, es correcto cambiarlo o eliminarlo, y puede que no sea realmente inútil. Para resumir audazmente, debemos verificar cuidadosamente.

4.8.3. Pasar parámetros de argc, argv y función principal

4.8.3.1 ¿Quién pasa parámetros a la función principal?

El proceso padre del programa donde se llama a la función principal pasa parámetros a la función principal y recibe el valor de retorno de main.

4.8.3.2 ¿Por qué necesito pasar parámetros a la función principal?

(1) Es posible que la función principal no pase parámetros, es decir, no es necesario que el proceso padre llame a la subrutina y pase parámetros a la subrutina. Esta forma de int main (void) significa que creemos que es innecesario pasar parámetros a main.
(2) A veces queremos que el programa tenga una especie de flexibilidad, por lo que elegimos controlar el funcionamiento del programa pasando parámetros al ejecutar el programa, para lograr el efecto de cambiar el resultado del programa sin recompilar el programa.

4.8.3.3 En la superficie: ¿cómo pasar parámetros a main?

(1) Pasar parámetros a main se logra a través de los dos parámetros reservados en lenguaje C argc y argv
(2)argc es un tipo int, lo que significa que se pasan varios parámetros a la función principal cuando se ejecuta el programa; argv es una matriz de cadenas, esta matriz se usa para almacenar varias cadenas y cada cadena es un parámetro que pasamos a la función principal. argv [0] es el primer parámetro pasado a la función principal, y argv [1] es el segundo parámetro pasado a main ...
#include <stdio.h>
#include <string.h>
// Al pasar parámetros a main, puede elegir cuál ejecutar cuando el programa se está ejecutando para obtener resultados de ejecución diferentes
// int main (void)
int main (int argc, char * argv [])
{
int i = 0;
printf ("El número de parámetros pasados ​​a la función principal es:% d. \ n", argc);
for (i = 0; i <argc; i ++)
{
printf (" % d parámetros son% s. \ n ", i, argv [i]);
}
if (argc! = 2)
{
printf (" necesitamos 2 param. \ n ");
return -1;
}
if (! strcmp (argv [1], "0"))
{
printf ("niño \ n");
}
else if (! strcmp (argv [1], "1"))
{
printf ("niña \ n");
}
else
{
printf ("desconocido \ n");
}
return 0;
}

4.8.3.4 Básicamente: ¿Cómo pasar parámetros a main?

(1) Hay varios métodos para llamar a un programa, pero en esencia, el proceso padre bifurca un proceso hijo, y luego el proceso de texto está vinculado a un programa para su ejecución (familia de funciones ejecutivas). Podemos pasarle parámetros al mismo tiempo cuando ejecute exec.
(2) Los parámetros que se pueden pasar cuando se llama al programa (es decir, los parámetros de main) son compatibles con el nivel del sistema operativo.

4.8.3.5 ¿A qué debo prestar atención al pasar parámetros a main?

(1) Los parámetros de la función principal se pasan a través de cadenas.
(2) Los parámetros se pasan cuando se llama al programa y los parámetros están separados por espacios.
(3) Si se va a utilizar argv dentro del programa, primero se debe marcar argc.

4.8.4. La esencia del tipo vacío

4.8.4.1, el lenguaje C es un lenguaje fuertemente tipado

(1) Hay dos tipos de lenguajes de programación: lenguajes fuertemente tipados y lenguajes débilmente tipados. Todas las variables en un lenguaje fuertemente tipado tienen su propio tipo fijo. Este tipo tiene una huella de memoria fija y un método de análisis fijo. No existe un concepto de tipo en un lenguaje débilmente tipado. Todas las variables son del mismo tipo (generalmente cadenas ), cuando el programa esté en uso, procesará las variables según sea necesario.
(2) El lenguaje C es un lenguaje típico fuertemente tipado, todas las variables en el lenguaje C tienen tipos claros. Debido a que una variable en el lenguaje C debe corresponder a una sección de memoria en la memoria, el compilador necesita el tipo de esta variable para determinar el número de bytes de memoria ocupados por esta variable y el método de análisis de esta sección de memoria.

4.8.4.2 ¿Cuál es el significado esencial del tipo de datos?

(1) La naturaleza del tipo de datos determina el uso de memoria de la variable y el método de análisis de memoria.
(2) Entonces se concluye que la variable en lenguaje C debe tener un cierto tipo de datos. Si una variable no tiene un cierto tipo (los llamados sin tipo), el compilador no podrá asignar memoria para esta variable y no podrá resolver la variable correspondiente. Memoria. Entonces se concluye que no puede haber variables sin tipos.
(3) Pero no puede haber tipos de memoria en lenguaje C. Antes de que la memoria esté vinculada a una variable específica, la memoria no puede tener ningún tipo. De hecho, la memoria pura no tiene tipo, la memoria tiene un tipo determinado solo porque está asociada con variables específicas (de hecho, la memoria en sí no es conocida y el compilador sabe que nuestro programa conoce el tipo cuando usa esta memoria. Entonces leerá y escribirá en la memoria de acuerdo con el significado del tipo).

4.8.4.3, ¿la esencia del tipo vacío?

(1) El significado correcto del tipo vacío es: no sé el tipo, el tipo es incierto y el tipo no se ha determinado.
(2) void a; define una variable de tipo void, lo que significa que a es una variable y a debe tener un cierto tipo, pero todavía no conozco el tipo de a, así que no estoy seguro, así que lo marco como vacío.

4.8.4.4 ¿Por qué se necesita el tipo vacío?

(1) ¿En qué circunstancias se requieren tipos nulos? De hecho, es necesario utilizar el tipo vacío al describir una sección de la memoria que no se ha utilizado específicamente.
(2) Un caso de aplicación típico de void es el valor de retorno de malloc. Sabemos que la función malloc solicita una sección de memoria del administrador de almacenamiento dinámico del sistema para que la utilice el programa actual. Malloc devuelve un puntero que apunta a la sección de memoria solicitada. La memoria que acaba de solicitar malloc no se ha utilizado para almacenar datos y la función malloc no puede predecir qué tipo de datos se almacenarán en esta memoria en el futuro, por lo que malloc no puede devolver un puntero de un tipo específico. La solución es devolver un tipo (void ) y decirle al exterior Lo que devuelvo es un espacio de memoria limpio y el tipo aún no se ha determinado. Entonces podemos leer y escribir cualquier tipo de datos en esta memoria después de malloc.
(3) La memoria apuntada por el puntero del tipo (void * ) aún no ha determinado el tipo, por lo que podemos usar la conversión de tipo forzada para forzarla a varios tipos en el futuro. Este es el destino final del tipo vacío, que debe ser coaccionado en un tipo concreto.
(4) Generalmente, void * se usa cuando se usan tipos de void, no solo void.

4.8.5. NULL en lenguaje C

4.8.5.1, la definición estándar de NULL en C / C ++

(1) NULL no es una palabra clave del lenguaje C, es esencialmente una definición de macro
(2) NULL definición estándar:
#ifdef _cplusplus // Compilación condicional
#define NULL 0
#else
#define NULL (void ) 0 // Esto corresponde al lenguaje C Explicación de la situación
#endif
: en el entorno de compilación de C ++, el compilador define una macro _cplusplus de antemano, y la compilación condicional se puede utilizar en el programa para determinar si el entorno de compilación actual es C ++ o C.
La esencia del análisis NULL: <La esencia de NULL es 0, pero este 0 no se analiza como un número, sino como una dirección de memoria. Este 0 es en realidad 0x00000000, que representa la dirección 0 de la memoria. (void
) 0 Esta expresión general representa un puntero, esta variable de puntero en sí ocupa 4 bytes, donde la dirección depende de la propia variable de puntero, pero el valor de esta variable de puntero es 0, es decir, esta variable de puntero apunta a la dirección 0 (actual Es una sección de memoria que comienza en la dirección 0). >

4.8.5.2, comprender la esencia de NULL desde el punto de vista de los punteros
(1)int *p;      // p是一个函数内的局部变量,则p的值是随机的,也就是说p是一个野指针。
(2)int *p = NULL;   // p是一个局部变量,分配在栈上的地址是由编译器决定的,我们不必关心,但是p的值是(void *)0,实际就是0,意思是指针p指向内存的0地址处。这时候p就不是野指针了。

(3) ¿Por qué un puntero salvaje debería apuntar a la dirección de memoria 0?
La razón principal es que en la mayoría de las CPU, la dirección 0 de la memoria no es accesible a voluntad (generalmente el sistema operativo controla estrictamente el área, por lo que las aplicaciones no pueden acceder a ella a voluntad). Por lo tanto, el puntero salvaje apunta a esta área para garantizar que el puntero salvaje no cause lesiones accidentales. Si el programa desreferencia inconscientemente el puntero salvaje en la dirección 0, activará una falla de segmentación. Esto le pedirá que le ayude a encontrar errores en el programa.

4.8.5.3. ¿Por qué necesitamos NULL?

(1) La primera función es hacer que el puntero salvaje apunte a la dirección 0 de forma segura.
(2) La segunda función es una marca especial. Los pasos para usar el puntero estándar son:

int *p = NULL;      // 定义p时立即初始化为NULL
p = xx;
if (NULL != p)
    {
        *p              // 在确认p不等于NULL的情况下才去解引用p
    }
p = NULL            // 用完之后p再次等于NULL

Nota: Generalmente, comparar un puntero y NULL no se escribe como si (p == NULL), sino como si (NULL == p). La razón es que si escribe accidentalmente == como = en la primera forma de escritura, el compilador no informará un error, pero el significado del programa es completamente diferente; mientras que en la segunda forma de escritura, si escribe accidentalmente == como =, el compilador Encontrará e informará errores.

4.8.5.4. Tenga cuidado de no mezclar NULL y '\ 0'

(1) '\ 0' y '0' se distinguen de 0 y NULL.
(2) '\ 0' es un carácter de escape, y su valor de código ASCII correspondiente es 0, que es esencialmente 0
(3) '0' es un carácter, y su valor de código ASCII correspondiente es 48, que es esencialmente 48
(4) 0 es un número, es 0 y la esencia es 0
(4) NULL es una expresión, que es una conversión de tipo forzada a void * tipo 0, que es esencialmente 0.
Resumen: El uso de '\ 0' es el final de una cadena en lenguaje C Bandera, generalmente se usa para comparar los caracteres en la cadena para determinar si la cadena ha llegado al final; '0' es el carácter 0, correspondiente al código ASCII del carácter 0, generalmente se usa para obtener el valor del código ASCII de 0; 0 es un número, generalmente se usa Para comparar si un número de tipo int es igual a 0; NULL es una expresión, generalmente utilizada para comparar si el puntero es un puntero salvaje.

4.8.6. Variables anónimas temporales en operaciones

4.8.6.1. La diferencia entre lenguaje C y ensamblador (ensamblado corresponde completamente a operaciones de máquina, C corresponde a operaciones lógicas)

(1) El lenguaje C se denomina lenguaje de alto nivel y el lenguaje ensamblador se denomina lenguaje de bajo nivel.
(2) Lenguaje de bajo nivel significa que el lenguaje ensamblador corresponde al funcionamiento de la máquina. El lenguaje ensamblador es solo el mnemónico del código de máquina de la CPU. Los programas de escritura en lenguaje ensamblador deben tener la mente de una máquina. Debido a que las diferentes CPU están diseñadas con conjuntos de instrucciones muy diferentes, las diferencias en la programación del ensamblaje son muy diferentes.
(3) Lenguaje de alto nivel (lenguaje C) Encapsula el lenguaje de bajo nivel (compilador de lenguaje C) y proporciona a los programadores algunas características gramaticales cercanas al pensamiento humano. Los seres humanos no necesitan pensar demasiado en los principios de las máquinas, pero pueden seguir Programación con sus propios principios lógicos. Por ejemplo, matrices, estructuras, punteros ...
(4) Los lenguajes de nivel superior como java, C #, etc. solo fortalecen aún más la sintaxis de la interfaz de operación fácil de usar proporcionada por el lenguaje C, y mejoran la facilidad de uso y la seguridad .

4.8.6.2 Algunas "pequeñas acciones" en lenguaje C

(1) Hay algunos elementos en el lenguaje de alto nivel que no están en la máquina.
(2) El lenguaje de alto nivel nos permite operar una amplia gama de operaciones. Significa que una operación que requiere varios pasos para completarse en un idioma de bajo nivel se puede completar en un paso en un idioma de alto nivel. Por ejemplo, en lenguaje C, una variable i debe incrementarse en 1, y en C, solo se necesita i ++. Parece que solo hay una línea de código. Pero, de hecho, la traducción a la etapa de ensamblaje requiere tres pasos para completarse: el primer paso es leer i de la memoria al registro, el segundo paso es agregar 1 a la i en el registro y el tercer paso es escribir la i agregada a la memoria de nuevo a la memoria. YO.

4.8.6.3, use variables temporales para comprender la conversión de tipo forzada
#include <stdio.h>

int main(void)
    {
        int *p = (int *)malloc(4);      // 由void *强制转为int *,不会警告

    /*
    void a;
    a = 5;
    */

    /*
    void a;
    int b = (int)a;
    b = 23;
    */

    return 0;
}
4.8.6.4. Utilice variables temporales para comprender las operaciones entre diferentes tipos de datos
#include <stdio.h>
int main(void)
{
    int b;
    float a;
    b = 10;
    a = b / 3;      // 第一步先算 b/3,第二步将第一步的结果强制类型转换为float生成一个临时变量,第三步将第二步生成的临时变量赋值给a,第四步销毁临时变量。
    printf("a = %f.\n", a);
        /*
        int b;
        float a, f;
        b = 10;
        f = 1.5;
        a = b / f;
        printf("a = %f.\n", a);
        */
        /*
        float a = 12.34;
        int b = (int)a;         // a被强制类型转换后自己本身竟然没变
        // (int)a强制类型转换并赋值在底层实际分了4个步骤:
        第一步先在另外的地方找一个内存构建一个临时变量x(x的类型是int,x的值等于a的整数部分),
        第二步将float a的值的整数部分赋值给x,
        第三步将x赋值给b,
        第四步销毁x。
        // 最后结果:a还是float而且值保持不变,b是a的整数部分
        printf("a = %f, b = %d.\n", a, b);
        */
        return 0;
    }

4.8.7. Estructura de la secuencia

4.8.7.1. La estructura de secuencia más obvia: una de las tres estructuras

(1) Si no se encuentra ningún salto o bucle de juicio cuando se ejecuta el código, se ejecuta secuencialmente de forma predeterminada. Después de ejecutar la oración anterior, se ejecutará la siguiente.
(2) La estructura de secuencia muestra el estado de funcionamiento de la CPU, que es ejecutar todas las declaraciones de código en secuencia en el eje de tiempo hasta que se detiene.

4.8.8 Macro de depuración para depuración de programas.

4.8.7.2 Estructura de selección y secuencia dentro de la estructura de bucle
(1) Por ejemplo, si () {} es un segmento de código de if inside {}, se ejecuta de acuerdo con la estructura de secuencia dentro del segmento de código.
(2) Lo mismo ocurre dentro de la caja del interruptor, que también se ejecuta en una estructura secuencial.
(3) El interior de while for también se ejecuta de acuerdo con la estructura de secuencia.

4.8.7.3. Estructura de la secuencia durante la compilación

(1) Un programa en C se compone de varios archivos .c, y varios archivos .c se compilan de forma independiente durante la compilación. Cuando se compila cada archivo c, el compilador lo compila línea por línea de adelante hacia atrás.
(2) La compilación secuencial durante la programación del compilador hará que las funciones / variables se definan / declaren antes de que puedan ser llamadas. Esta es también la fuente de las declaraciones de funciones / variables en lenguaje C.
(3) ¿Durante el proceso de vinculación? Debe decirse que el enlazador se completa realmente bajo la guía del script de enlace. Entonces, el orden de los archivos .o cuando se vinculan lo especifica el script de vinculación. Si el orden está claramente especificado en el script de enlace, las reglas se priorizarán para ser ordenadas en el orden especificado. Si el orden específico no se especifica en el script de enlace, el enlazador lo organizará automáticamente.

4.8.7.4 Pensamiento: ¿Por qué es la esencia de la estructura de la secuencia?

(1) La estructura de secuencia simboliza esencialmente el principio de diseño de la CPU, y la CPU está diseñada por humanos, por lo que el diseño de la CPU se ajusta al principio del pensamiento humano.

4.8.8. Depurar macros para depurar programas

4.8.8.1 Soluciones comunes para la depuración de programas: depuración de un solo paso, depuración de LED sin sistema operativo, información de impresión, archivo de registro

(1) La depuración de un solo paso utilizando un depurador (como IDE, Jlink) es adecuada para principiantes. La mayor ventaja es que es intuitiva y puede ayudar a encontrar problemas. La desventaja es que es restrictiva y lenta.
(2) El bare metal utiliza LED, zumbador y otra depuración de hardware, que es adecuado para el programa bare metal del microordenador de un solo chip.
(3) La función printf se utiliza para imprimir y depurar. Como programador, debe aprender a utilizar la información impresa para depurar. La ventaja es que es universal y se puede utilizar en casi todas las situaciones.
(4) El archivo de registro (archivo de registro) es para imprimir cierta información de depuración en un momento específico durante el funcionamiento del sistema, y ​​el archivo de registro registra esta información de depuración para la búsqueda posterior y el seguimiento de problemas. Es adecuado para depuración de programas a gran escala o a nivel de sistema.

4.8.8.2, la información impresa no puede ser demasiado ni demasiado poca

(1) Muy poca información de depuración no tendrá suficiente información para encontrar el problema.
(2) Demasiada información de depuración abrumará la información inútil con una gran cantidad de información inútil, lo que hará que la información útil sea invisible, lo que no significa nada.

4.8.8.3, la diferencia entre la versión de depuración (DEBUG) y la versión de lanzamiento (RELEASE)

(1) La versión DEBUG es la versión que contiene la salida de información de depuración. La versión de depuración se publicará durante la prueba del programa. Esta versión del programa imprimirá la información de depuración / archivo de registro cuando el programa se esté ejecutando. Esta información puede ayudar al evaluador a juzgar el problema del programa Dónde. La desventaja de la versión DEBUG es que la información de depuración de salida ocupa recursos del sistema y ralentiza la velocidad de funcionamiento del sistema. Por lo tanto, el rendimiento de la versión DEBUG es menor que la versión RELEASE.
(2) La versión RELEASE es la versión final. En comparación con la versión DEBUG, el código de función es el mismo, pero se elimina toda la información de depuración. Es conveniente que la prueba final pase el programa que se lanzará, ya que se elimina la información de depuración, la eficiencia de ejecución del programa es mayor.
(3) Las versiones DEBUG y RELASE son en realidad un conjunto de código fuente. Hay muchas declaraciones que imprimen información de depuración en el código fuente ¿Cómo controlar la generación de versiones DEBUG y RELEEASE? Confíe en la compilación condicional, confíe en una macro.

4.8.8.4, el principio de realización de la macro de depuración

(1) El principio general de la macro DEBUG es:
#ifdef DEBUG
#define dbg () printf ()
#else
#define dbg ()
#endif
(2) El método de trabajo es: si queremos generar la versión DEBUG, agréguela antes de la declaración de compilación condicional Simplemente agregue #define DEBUG, para que la declaración de depuración dbg () en el programa sea reemplazada por printf para la salida; si queremos generar la versión RELEASE, elimine #define DEBUG, luego dbg () será reemplazado por vacío, luego el programa Todas las sentencias dbg () del archivo se evaporan directamente, de modo que cuando se compila el programa, generará código sin información de depuración.

4.8.8.5. Cómo utilizar la macro de depuración

En Internet, Baidu, CSDN, el autor dijo que es mejor. No lo repetiré aquí.

4.5.8.6. Analizar varias macros DEBUG

(1) Macro DEBUG en la
aplicación #ifdef DEBUG
#define DBG (...) fprintf (stderr, "DBG (% s,% s (),% d):", FILE , FUNCTION , LINE ); fprintf (stderr , __VA_ARGS )
#else
#define DBG (...)
#endif
Nota:
ARCHIVO etc., están macros en lenguaje C, lo que significa que esta cosa es una definición de macro predefinido, sino que se define por C lenguaje mismo. Estas macros tienen significados especiales. Por ejemplo, FILE__ representa el nombre del archivo c que se está compilando actualmente.

(2) 内核 中 的 DEBUG 宏
#ifdef DEBUG_S3C_MEM
#define DEBUG (fmt, args ...) printk (fmt, ## args)
#else
#define DEBUG (fmt, args ...) do {} while (0)
#terminara si

Supongo que te gusta

Origin blog.51cto.com/14762640/2542837
Recomendado
Clasificación