Comprender el C integrado:¿Qué son las estructuras?
Después de presentar las estructuras, veremos algunas de las aplicaciones importantes de este poderoso objeto de datos. Luego, examinaremos la sintaxis del lenguaje C para declarar una estructura. Por último, presentaremos brevemente el requisito de alineación de datos. Veremos que podemos reducir el tamaño de una estructura simplemente reorganizando el orden de sus miembros.
Este artículo proporciona información básica sobre las estructuras en la programación C integrada.
Después de presentar las estructuras, veremos algunas de las aplicaciones importantes de este poderoso objeto de datos. Luego, examinaremos la sintaxis del lenguaje C para declarar una estructura. Por último, presentaremos brevemente el requisito de alineación de datos. Veremos que podemos reducir el tamaño de una estructura simplemente reorganizando el orden de sus miembros.
Estructuras
Varias variables del mismo tipo que están relacionadas lógicamente entre sí se pueden agrupar como una matriz. Trabajar en un grupo en lugar de una colección de variables independientes nos permite organizar los datos y usarlos de manera más conveniente. Por ejemplo, podemos definir la siguiente matriz para almacenar las últimas 50 muestras de un ADC que digitaliza una entrada de voz:
uint16_t voz [50];
Tenga en cuenta que uint16_t es un tipo entero sin signo con un ancho de exactamente 16 bits. Esto se define en la biblioteca estándar de C stdint.h , que proporciona tipos de datos de una longitud de bits específica independientemente de las especificaciones del sistema.
Las matrices se pueden usar para agrupar varias variables que son del mismo tipo de datos. ¿Qué pasa si hay una conexión entre variables de diferentes ¿tipos de datos? ¿Podemos tratar estas variables como un grupo en nuestro programa? Por ejemplo, suponga que necesitamos especificar la frecuencia de muestreo del ADC que genera la voz matriz de arriba. Podemos definir una variable flotante para almacenar la frecuencia de muestreo:
float sample_rate;
Aunque las variables voz y sample_rate están relacionados entre sí, se definen como dos variables independientes. Para asociar estas dos variables entre sí, podemos usar una poderosa construcción de datos del lenguaje C llamada estructura. Las estructuras nos permiten agrupar diferentes tipos de datos y tratarlos como un solo objeto de datos. Una estructura puede incluir diferentes tipos de tipos de variables, como otras estructuras, punteros a funciones, punteros a estructuras, etc. Para el ejemplo de voz, podemos usar la siguiente estructura:
struct record {uint16_t voice [50]; float sample_rate;};
En este caso, tenemos una estructura llamada registro que tiene dos miembros o campos diferentes:el primer miembro es una matriz de uint16_t elementos, y el segundo miembro es una variable de tipo float. La sintaxis comienza con la palabra clave struct . La palabra después de la palabra clave struct es un nombre opcional que se usa para hacer referencia a la estructura más adelante. Discutiremos otros detalles de la definición y el uso de estructuras en el resto del artículo.
¿Por qué son importantes las estructuras?
El ejemplo anterior señala una aplicación importante de estructuras, es decir, definir objetos de datos dependientes de la aplicación que pueden asociar variables individuales de diferentes tipos entre sí. Esto no solo conduce a una forma eficiente de manipular los datos, sino que también nos permite implementar estructuras especializadas llamadas estructuras de datos.
Las estructuras de datos se pueden utilizar para diversas aplicaciones, como la mensajería entre dos sistemas integrados y el almacenamiento de datos recopilados de un sensor en ubicaciones de memoria no contiguas.
Figura 1. Las estructuras se pueden utilizar para implementar una lista vinculada.
Además, las estructuras son objetos de datos útiles cuando el programa necesita acceder a los registros de un periférico de microcontrolador mapeado en memoria. Veremos las aplicaciones de estructura en el próximo artículo.
Figura 2. Mapa de memoria de una MCU STM32. Imagen cortesía de Embedded Systems with ARM.
Declaración de una estructura
Para usar estructuras, primero necesitamos especificar una plantilla de estructura. Considere el código de ejemplo a continuación:
struct record {uint16_t voice [4]; float sample_rate;};
Esto especifica un diseño o plantilla para crear las futuras variables de este tipo. Esta plantilla incluye una matriz de uint16_t y una variable de tipo float. El nombre de la plantilla es registro , y esto viene después de la palabra clave struct . Vale la pena mencionar que no hay asignación de memoria para almacenar una plantilla de estructura. La asignación de memoria ocurre solo después de que se define una variable de estructura basada en este diseño. El siguiente código declara la variable mic1 de la plantilla anterior:
struct record mic1;
Ahora, se asigna una sección de memoria para la variable mic1 . Tiene espacio para almacenar los cuatro uint16_t elementos de la matriz y una variable flotante.
Se puede acceder a los miembros de una estructura mediante el operador de miembros (.). Por ejemplo, el siguiente código asigna 100 al primer elemento de la matriz y copia el valor de sample_rate al fs variable (que debe ser de tipo float).
mic1.voice [0] =100; fs =mic1.sample_rate;
Otras formas de declarar una estructura
Observamos una forma de declarar estructuras en la sección anterior. El lenguaje C admite algunos otros formatos que se revisarán en esta sección. Probablemente se apegará a un formato en todos sus programas, pero estar familiarizado con los otros puede ser útil en ocasiones.
La sintaxis general para declarar la plantilla de una estructura es:
struct tag_name {type_1 member_1; type_2 member_2; … Type_n member_n;} variable_name;
El nombre_etiqueta y nombre_variable son identificadores opcionales. Por lo general, veremos al menos uno de estos dos identificadores, pero hay casos en los que podemos eliminarlos.
Sintaxis 1: Cuando ambos tag_name y nombre_variable están presentes, estamos definiendo la variable de estructura justo después de la plantilla. Usando esta sintaxis, podemos reescribir el ejemplo anterior de la siguiente manera:
struct record {uint16_t voice [4]; float sample_rate;} mic1;
Ahora, si necesitamos definir otra variable ( mic2 ), podemos escribir
struct record mic2;
Sintaxis 2: Solo nombre_variable está incluido. Usando esta sintaxis, podemos reescribir el ejemplo de la sección anterior de la siguiente manera:
struct {uint16_t voice [4]; float sample_rate;} mic1;
En este caso, tenemos que definir todas nuestras variables justo después de la plantilla y no podemos definir ninguna otra variable más adelante en nuestro programa (porque la plantilla no tiene un nombre y no podemos hacer referencia a ella más adelante).
Sintaxis 3: En este caso, no hay tag_name o nombre_variable . Las plantillas de estructura definidas de esta manera se denominan estructuras anónimas. Una estructura anónima se puede definir dentro de otra estructura o unión. A continuación se ofrece un ejemplo:
struct test {// Estructura anónima struct {float f; char a; };} test_var;
Para acceder a los miembros de la estructura anónima anterior, podemos usar el operador de miembros (.). El siguiente código asigna 1.2 al miembro f .
test_var.f =1.2;
Dado que la estructura es anónima, accedemos a sus miembros utilizando el operador de miembros solo una vez. Si tuviera un nombre como en el siguiente ejemplo, tendríamos que usar el operador miembro dos veces:
struct test {struct {float f; char a; } anidado;} test_var;
En este caso, deberíamos usar el siguiente código para asignar 1.2 a f :
test_var.nested.f =1.2;
Como puede ver, las estructuras anónimas pueden hacer que el código sea más legible y menos detallado. También es posible utilizar la palabra clave typedef junto con una estructura para definir un nuevo tipo de datos. Veremos este método en un artículo futuro.
Disposición de memoria para una estructura
El estándar C garantiza que los miembros de una estructura se ubicarán en la memoria uno tras otro en el orden en que se declaran los miembros dentro de la estructura. La dirección de memoria del primer miembro será la misma que la dirección de la estructura misma. Considere el siguiente ejemplo:
struct Test2 {uint8_t c; uint32_t d; uint8_t e; uint16_t f;} MyStruct;
Se asignarán cuatro ubicaciones de memoria para almacenar las variables c, d, e y f. El orden de las ubicaciones de memoria coincidirá con el de declarar los miembros:la ubicación de c tendrá la dirección más baja, luego d, e, y finalmente, aparecerá f. ¿Cuántos bytes necesitamos para almacenar esta estructura? Teniendo en cuenta el tamaño de las variables, sabemos que, al menos, se requieren 1 + 4 + 1 + 2 =8 bytes para almacenar esta estructura. Sin embargo, si compilamos este código para una máquina de 32 bits, sorprendentemente observaremos que el tamaño de MyStruct es 12 bytes en lugar de 8! Esto se debe al hecho de que un compilador tiene ciertas restricciones al asignar memoria para diferentes miembros de una estructura. Por ejemplo, un entero de 32 bits se puede almacenar solo en ubicaciones de memoria cuya dirección sea divisible por cuatro. Dichas restricciones, denominadas requisitos de alineación de datos, se implementan para permitir que el procesador acceda a las variables de manera más eficiente. La alineación de datos conduce a un desperdicio de espacio (o relleno) en el diseño de la memoria. Este tema solo se presenta aquí; repasaremos los detalles en el próximo artículo de esta serie.
Figura 3. La alineación de datos conduce a un desperdicio de espacio (o relleno) en el diseño de la memoria.
Al conocer los requisitos de alineación de datos, es posible que podamos reorganizar el orden de los miembros dentro de una estructura y hacer que el uso de la memoria sea más eficiente. Por ejemplo, si reescribimos la estructura anterior como se indica a continuación, su tamaño disminuirá a 8 bytes en una máquina de 32 bits.
struct Test2 {uint32_t d; uint16_t f; uint8_t c; uint8_t e;} MyStruct;
Para un sistema integrado con limitación de memoria, es un ahorro significativo reducir el tamaño de un objeto de datos de 12 bytes a 8 bytes, particularmente cuando un programa requiere muchos de estos objetos de datos.
El próximo artículo discutirá la alineación de datos con mayor detalle y examinará algunos ejemplos de uso de estructuras en sistemas embebidos.
Resumen
- Las estructuras nos permiten definir objetos de datos dependientes de la aplicación que pueden asociar variables individuales de diferentes tipos entre sí. Esto conduce a un medio eficiente de manipular los datos.
- Las estructuras especializadas, llamadas estructuras de datos, se pueden utilizar para diversas aplicaciones, como la mensajería entre dos sistemas integrados y el almacenamiento de datos recopilados de un sensor en ubicaciones de memoria no contiguas.
- Las estructuras son útiles cuando necesitamos acceder a los registros de un periférico de microcontrolador mapeado en memoria.
- Es posible que podamos hacer que el uso de la memoria sea más eficiente reorganizando el orden de los miembros dentro de una estructura.
Para ver una lista completa de mis artículos, visite esta página.
Incrustado
- ¿Qué son los metales refractarios?
- ¿Qué son las bisagras plegables?
- ¿Qué son los tornillos de plataforma?
- ¿Qué son los sellos energizados por resorte?
- ¿Qué son los tornillos para madera?
- ¿Qué son las aleaciones de acero?
- ¡¿Qué hago con los datos ?!
- Java - Estructuras de datos
- ¿Qué es IIoT?
- ¿Qué son los datos de mantenimiento?
- Entendiendo la carpintería