Uso estático en C51

Elija

Código escrito por no programadores

Siempre habrá errores, no hay mejor en este sentido, solo mejor. La eficiencia es lo que los programadores deben hacer, y la ausencia de errores es lo que los programadores buscan a lo largo de sus vidas. Reutilizar, dividir y conquistar y comprometerse son las ideas básicas de la filosofía del código. La modularidad y la orientación a objetos son formas de lograr un código eficiente y libre de errores. Un código eficiente y libre de errores requiere práctica y pensamiento iterativo constante.

1.2.1 Convención de nomenclatura

La especificación del comando básicamente adopta la nomenclatura húngara recomendada por Microsoft, que está ligeramente simplificada.

1. Constantes

Las constantes constan de letras mayúsculas y números, separados por guiones bajos, como CPU_8051.

2. Variable

Las variables comienzan con una letra minúscula (tipo de variable) y están separadas por letras mayúsculas. Puede agregar prefijos de dominio variables (los prefijos de dominio activos variables están separados por guiones bajos). Tales como: v_nAcVolMin (voltaje mínimo de CA).

Los prefijos de los campos variables se muestran en la siguiente tabla.

Para las variables locales, si el significado del nombre de la variable es obvio, no se agrega ningún prefijo para evitar complicaciones. Por ejemplo, las variables int I, j, k se utilizan para bucles; coordenadas tridimensionales de punto flotante (x, y, z), etc.

3. Los nombres de funciones generalmente comienzan con una letra mayúscula y están separados por letras mayúsculas, como SetSystemPara. La denominación de funciones adopta la forma verbo-objeto. Si la función está en la parte inferior, considere usar todas las palabras subrayadas en minúsculas. Por ejemplo, las funciones gráficas subyacentes: píxel, línea a y función de lectura del teclado get_key, etc.

4. Los nombres de los símbolos deben ser genéricos o tener significados específicos y ser legibles. Especialmente el significado de las variables globales y las variables estáticas debe ser claro. Algunas palabras clave en C++ no se pueden utilizar como nombres de símbolos, como clase, nuevo, amigo, etc. La longitud del nombre del símbolo es inferior a 31, lo que es consistente con ANSI C. El nombre solo puede constar de 26 letras, 10 números y el guión bajo "_". No utilice símbolos como "$" y "@". El subrayado '_' debe ser llamativo, no al principio ni al final del símbolo, solo en el medio del símbolo y no aparecer dos veces seguidas.

5. Hay muy pocos números sin sentido en el programa y las constantes deben reemplazarse con macros tanto como sea posible.

1.2.2 Uso de aserciones

Los programas generalmente se dividen en versiones de depuración y versiones de lanzamiento. La versión de depuración se utiliza para la depuración interna y la versión de lanzamiento se publica para los usuarios.

Assert es una macro que sólo funciona en compilaciones de depuración y se utiliza para comprobar cosas que no deberían suceder. A continuación se muestra un programa de copia de memoria. Durante el proceso de ejecución, si el parámetro de afirmación es falso, el programa se detendrá (normalmente aparecerá un cuadro de diálogo que indica dónde se activó la afirmación).

//Copiar bloques de memoria que no se superpongan

void memcpy(void *pvTo, void *pvFrom, size_t size)

{

void * pbTo =(byte *)pvTo;

void * Pb from =(byte *)PV from;

Aserción (pvTo!= NULL & amp& amppvFrom!= NULL) ;

mientras(tamaño---gt; 0 )

* pbto++ = * Pb de++;

Retorno (pvTo);

}

Assert no es una macro creada apresuradamente. Para no causar diferencias entre las versiones de depuración y lanzamiento del programa, afirmar no debería tener ningún efecto secundario. Entonces afirmar no es una función, sino una macro. Los programadores pueden considerar la afirmación como un método de prueba inofensivo que se puede utilizar de forma segura en cualquier estado del sistema.

Los siguientes son algunos principios para usar aserciones:

1) Utilice aserciones para detectar situaciones ilegales que no deberían ocurrir. No confundas situaciones ilegales con situaciones de error, estas últimas son inevitables y hay que afrontarlas.

2) Utilice aserciones para confirmar los parámetros de la función.

3) Al escribir una función, debes verificarla repetidamente y preguntarte "¿Qué suposiciones debo hacer?" Una vez determinadas las suposiciones, debes usar afirmaciones para verificarlas.

4) Los libros de texto generales alientan a los programadores a diseñar programas a prueba de errores, pero recuerde que este estilo de programación ocultará los errores. Al realizar programación a prueba de errores, debe utilizar aserciones para generar una alerta si sucede algo "imposible".

1.2.3 Optimización/Eficiencia

Regla 1: Aplicar definiciones volátiles a variables globales utilizadas en funciones/hilos de interrupción y funciones externas. Por ejemplo:

Puntos de transacción de enteros variables;

Voidtimer (void) interrupción 1//procesador de interrupción

{

Tick++

}

Espera no válida (intervalo intermedio)

{

tick = 0;

while(tick & lt intervalo);

}

Si no se utiliza volátil, dado que el bucle while es un bucle vacío, el compilador optimizará el bucle para que no funcione después de la optimización (el compilador no ¡No sé que esta variable se usa en la interrupción)! Obviamente esto está mal.

Regla 2: No escribas declaraciones demasiado complejas. El código C++/C compacto no dará como resultado un código de máquina eficiente, pero reducirá la comprensibilidad del programa y aumentará la probabilidad de errores en el programa.

Regla 3: Principios de aplicación en la programación de tipos variables: intente utilizar tipos pequeños (intente no utilizar "Float" si es posible) y tipos sin signo, porque las operaciones simbólicas llevan mucho tiempo al mismo tiempo; , funciones El valor de retorno tampoco está firmado, lo que aporta otro beneficio: evitar errores ocultos causados ​​por diferentes tipos de operaciones de comparación de datos.

1.2.4 Otros

Regla 1: No escriba una función con múltiples funciones y no mezcle valores normales e indicadores de error en el valor de retorno de la función.

Regla 2: No programar valores BOOL VERDADERO y FALSO correspondientes a 1 y 0. La mayoría de los lenguajes de programación definen FALSO como 0 y cualquier valor distinto de 0 como VERDADERO. Visual C++ define VERDADERO como 1 y Visual Basic define VERDADERO como -1. Por ejemplo:

Indicador booleano;

<…

If(flag) {//Hacer algo} //Uso correcto

if (flag = = true){//algo }/Uso peligroso

if(flag = = 1){//do algo }/Uso peligroso

If (!Flag) { //Hacer algo} //Uso correcto

if(flag = = false){//hacer algo}//Uso irrazonable

if(flag = = 0) {// haciendo algo }//Uso irrazonable

Regla 3: tenga cuidado de no escribir "==" como "=". El compilador no detectará automáticamente este error.

Regla 4: Se recomienda que el valor de retorno de la función unificada sea un entero sin signo, donde 0 indica que no hay error y otros indican el tipo de error.

1.3 Programación C modular

Aunque el lenguaje C no tiene el componente orientado a objetos de C++, aún debería absorber ideas orientadas a objetos y adoptar ideas de programación modular. El pensamiento orientado a objetos y el lenguaje orientado a objetos son dos conceptos. La programación orientada a objetos todavía se puede lograr en lenguajes no orientados a objetos. ¡Piensa en el nacimiento de C++!

C++ no tiene motivos para ser arrogante y prejuicioso contra C. No todas las situaciones son una buena solución al problema, como los requisitos duales de eficiencia y espacio en los sistemas integrados. Tenga en cuenta que estamos hablando de métodos, no de compiladores.

El principal problema de C en el desarrollo de software es la falta de control (encapsulación) del acceso a los datos. Los programadores de C utilizan felizmente una gran cantidad de variables globales de estilo externo para intercambiar datos entre módulos. "¡Qué conveniente!" El programador estará feliz de pasárselo al siguiente programador. Aparecieron tantas variables en muchos módulos, y el corte seguía siendo confuso, hasta que un día finalmente descubrí que era difícil encontrar una "persona". Cuando algo sabe bien, las personas inteligentes lo prueban y lo mejoran, mientras que los estúpidos simplemente mueren.

No hay salvador en este mundo. Para poner más esfuerzo en C, los programadores y fundadores de C ya lo han pensado.

Creo que hay primavera en los lirios. Echemos un vistazo a cómo el lenguaje C implementa un método de programación modular, con encapsulación y polimorfismo de características OO hasta cierto punto.

Antes de entrar en detalles, es necesario aclarar los conceptos de vida útil y visibilidad. La vida útil se refiere al ciclo de vida de una variable en la memoria y la visibilidad se refiere a si la variable está disponible en la ubicación actual. Los dos están estrechamente relacionados, pero no pueden confundirse. La existencia pero invisibilidad de una persona sólo puede explicarse como Dios o alma, y ​​no es de extrañar que exista una variable pero sea invisible. El enfoque modular aprovecha la capacidad de supervivencia y visibilidad especiales de funciones estáticas y variables estáticas.

Finalmente, lo que hay que dejar claro es que el módulo aquí se basa en un archivo .c.

Regla 1: Utilice reglas de nomenclatura de funciones y funciones estáticas.

Las funciones internas de un módulo que no son llamadas por otros módulos adoptan las siguientes reglas de nomenclatura: utilizar todas las palabras en minúsculas y subrayadas. Por ejemplo, las funciones gráficas subyacentes: píxel, línea a y función de lectura del teclado get_key, etc. Estas funciones deben definirse como funciones estáticas, de modo que cuando otros módulos llamen incorrectamente a estas funciones (como el compilador BC), el compilador pueda dar un error. (Nota: algunos compiladores no pueden informar errores, pero en aras de un estilo de código coherente y claridad de la jerarquía de funciones, se recomienda hacerlo).

Regla 2: Utiliza variables estáticas.

Las variables globales en un módulo que no pueden ser leídas o escritas por otros módulos deben declararse estáticamente para que el compilador pueda dar una advertencia cuando otros módulos lean o escriban incorrectamente estas variables (compilador C51) o error (BC compilador).

Regla 3: Introducir los conceptos de interfaz OO y parámetros de paso de puntero.

Las interfaces de datos (es decir, funciones) entre módulos deben considerarse completamente de antemano, qué interfaces se necesitan y qué datos deben operarse a través de las interfaces, y tratar de mantener las interfaces sin cambios.

El intercambio de datos entre módulos debe realizarse a través de interfaces tanto como sea posible pasando parámetros a través de funciones. Para garantizar una alta eficiencia del programa y reducir el espacio de la pila, se debe pasar una gran cantidad de parámetros (como estructuras) a través de direcciones, se deben usar punteros como parámetros de función o punteros de retorno de función y se deben evitar las variables globales en el forma de externos. Tenga en cuenta que las variables globales del formulario externo están permitidas y son obligatorias.

La sobrecarga adicional de pasar parámetros de puntero es principalmente el espacio de datos de los punteros y los punteros locales (los sistemas integrados (como C51) a menudo colocan parámetros de función en la pila de RAM externa debido al espacio limitado de la pila), lo que aumenta La sobrecarga del código son solo llamadas a funciones, lo que brinda una buena estructura modular. El uso de funciones de interfaz ahorrará mucho espacio en el código que el uso directo de variables globales en muchos lugares del código.

Las cosas que hay que tener en cuenta siempre tienen dos lados. El agua puede arrastrar un barco o volcarlo. Si las variables a las que es necesario acceder con frecuencia todavía se pasan a través de la interfaz, la sobrecarga de las llamadas a funciones es enorme. En este momento, deberíamos considerar seguir usando variables globales externas.

A continuación se muestran dos módulos C intercambiando datos:

//Módulo 1. C

one struct * void GetOneStruct(void); //Recopila la interfaz de datos del módulo 1

void SetOneStruct(one struct * pone struct); //Escribe la interfaz de datos del módulo 1

p>

p>

Estructura-estructura

{

int m? _ imember

//......

} t 1; //Datos del módulo 1

//Código de inicialización T1.... .

one struct * void GetOneStruct(void)

{

one struct * pt 1; //Solo define una variable local.

t 1 . I miembro = 15

pt 1 = & t 1; p>

void SetOneStruct(one struct * pone struct)

{

t 1. I miembro = pone struct-& gt;imember

/ /…….

}

//Módulo 2. C

void operar construct(void); //El módulo 2 opera los datos del módulo 1 a través de la interfaz proporcionada por el módulo 1.

una estructura * void GetOneStruct(void);

void SetOneStruct(una estructura * pone struct

void operar construct(void)

{

OneStruct * pt2//Defina solo una variable local.

pt2 = getone struct(); //Leer datos

SetOneStruct(pt2); //Sobrescribir datos

}

Usando Las interfaces para acceder a los datos pueden evitar algunos errores, porque el valor de retorno de una función solo puede ser un valor correcto, pero no una variable global.

Por ejemplo, cOneChar == 4; puede confundirse con cOneChar = 4;

Regla 4: Encapsulación y polimorfismo limitados

No olvides la fuente. de clases de C++ En comparación con la estructura de C, el mecanismo de función virtual de C++ es esencialmente un puntero de función. Para encapsular datos y métodos juntos y mejorar la reutilización del código, por ejemplo, para algunas estructuras de datos relacionadas con el hardware, se recomienda definir funciones que accedan a la estructura de datos como punteros de función dentro de la estructura. De esta forma, cuando el hardware cambia, es necesario reescribir las funciones que acceden al hardware. Siempre que la dirección de función reescrita se asigne al puntero de función, no es necesario mover el código avanzado en absoluto porque utiliza el puntero de función, lo que permite la reutilización del código. Además, los punteros de función se pueden pasar a código de alto nivel pasando parámetros o variables globales, lo cual es más conveniente. Por ejemplo:

Estructura-estructura

{

int m? _ imember

int (*func)(int, int);

//......

} t2