10- Arduino, Tipos de datos y Variables

Comencemos con los tipos de memoria del microcontrolador, hay tres de ellos:

  • Flash o ROM : memoria de solo lectura, memoria no vol谩til del microcontrolador, que almacena el c贸digo del programa, firmware. Funciones, procedimientos, operaciones, todo esto se queda ah铆 y no cambia durante la operaci贸n del firmware (puede, por supuesto, ingresar all铆, pero no lo haremos, y no lo necesitamos). Durante la descarga del firmware, el cargador de arranque escribe el firmware aqu铆.
  • SRAM o RAM : memoria de acceso aleatorio. Almacena variables cuyos valores pueden cambiar durante la operaci贸n, tenemos m谩s que libre acceso a esta memoria y la usaremos activamente. Despu茅s de reiniciar el microcontrolador, esta memoria se borra por completo .
  • EEPROM – Memoria de solo lectura programable y borrable el茅ctricamente: ROM programable borrable el茅ctricamente, memoria no vol谩til asignada al usuario para almacenar valores que se pueden cambiar, pero que no se restablecer谩n despu茅s de un reinicio. La cosa es conveniente, hay una lecci贸n aparte al respecto .

En un futuro pr贸ximo, solo nos interesar谩 la memoria SRAM, en la que se almacenan las variables, de ellas hablaremos m谩s adelante.


Sistema binario.

En el mundo digital, que tambi茅n incluye al microcontrolador, la informaci贸n se almacena, convierte y transmite en forma digital, es decir, en forma de ceros y unos. En consecuencia, una celda de memoria elemental que puede recordar 0 o 1 se llama bit. Por lo tanto, pasamos sin problemas al sistema binario de c谩lculo. 隆Vamos, recuerda la inform谩tica de la escuela! Sin entrar en detalles sobre 芦c贸mo funciona禄, intentemos considerar el patr贸n;

BinarioDecimal
00000
00011
00102
00113
01004
01015
01106
01117
10008
10019
1000016
Primeros d铆gitos binarios.

Etc. Adem谩s del patr贸n de d铆gitos y n煤meros crecientes, hay uno m谩s: observe m谩s de cerca los n煤meros en el sistema binario con todos los ceros a la derecha de uno:

102
1004
10008
1000016
Correspondencia binario decimal.

Es en las potencias de dos que muchas cosas est谩n ligadas en el mundo digital. Para obtener el total de n煤meros decimales que se pueden codificar con un n煤mero determinado de bits, eleve 2 a la potencia del n煤mero de bits. Miramos la tabla de arriba y continuamos:

  • 5 bits – 32
  • 6 bits – 64
  • 7 bits – 128
  • 8 bits – 256
  • 9 bits – 512
  • 10 bits – 1024

Etc. Inmediatamente debes recordar que en programaci贸n, el conteo comienza desde cero, es decir, con 5 bits podemos codificar un n煤mero decimal de 0 a 31, 8 bits – de 0 a 255, 10 bits – de 0 a 1023. Es muy importante entender y recordar esto, ser谩 muy 煤til m谩s adelante.

La siguiente unidad de medida m谩s grande en el mundo digital es un byte, que consta de 8 bits. 驴Por qu茅 8? Hist贸ricamente, los buses de los primeros microprocesadores ten铆an un ancho de 8 bits, por lo que probablemente este n煤mero se tom贸 como una unidad de memoria m谩s antigua. Adem谩s, 8 es 2 elevado a 3, lo cual es muy simb贸lico y conveniente. Y tambi茅n, para codificar todas las letras latinas, signos de puntuaci贸n, signos matem谩ticos y solo s铆mbolos (todo en el teclado), antes eran suficientes 7 bits (128 caracteres), pero luego se volvieron pocos, y se introdujo un bit adicional, el octavo. Es decir, 8 bits tambi茅n es el tama帽o de la tabla de caracteres, que se llama ASCII… Volveremos a ello en este cap铆tulo. Entonces, la pregunta de por qu茅 8 bits en un byte no tiene una respuesta clara, porque hay bytes de 6 bits y bytes de 9 bits … Pero estas son excepciones de los procesadores antiguos, en los dispositivos digitales modernos, un byte generalmente contiene 8 bits ( en diferentes arquitecturas AVR pueden ser diferentes), lo que le permite codificar 256 n煤meros decimales del 0 al 255, respectivamente. Entonces ya lo sabes con certeza:

  • 1 KB = 1024 B
  • 1 MB = 1024 kB
  • 1 GB = 1024 MB
  • Etc.

El sistema binario es nativo del microcontrolador, y hay una serie de herramientas para trabajar con bits individuales, hablaremos de ellas en la lecci贸n sobre operaciones de bits de la secci贸n de lecciones avanzadas.


Otros sistemas num茅ricos.

Los datos en la memoria del microcontrolador se almacenan en representaci贸n binaria, pero adem谩s, existen otros sistemas de c谩lculo en los que podemos trabajar. Trate de recordar de inmediato y comprender que no necesita traducir n煤meros de un sistema num茅rico a otro, a Arduino no le importa en absoluto en qu茅 formato alimenta el valor de una variable, se interpretar谩n autom谩ticamente en forma binaria. Se introducen diferentes sistemas de numeraci贸n principalmente para la conveniencia del programador.

Ahora, en esencia: arduino admite (y en general no necesita nada m谩s) cuatro sistemas num茅ricos cl谩sicos: binario, octal, decimal y hexadecimal. Un peque帽o recordatorio: el sistema hexadecimal tiene 16 valores por d铆gito, los primeros 10 son como decimales, el resto son las primeras letras del alfabeto latino: 0, 1, 2, 3, 4, 5, 6, 7, 8 , 9, a, b, c, d, e, f.

Con el sistema decimal, todo es simple, escribimos los n煤meros como se ven. 10 es diez, 25 es veinticinco y as铆 sucesivamente. El binario tiene el prefijo 0b (cero b) o B, es decir, el n煤mero binario 101 se escribir谩 en el Ide como 0b 101 o B 101. Octal tiene el prefijo 0 (cero), por ejemplo 012. El hexadecimal tiene el prefijo 0x (cero x), FF19 se escribir谩 como 0x FF19.

BasePrefijoEjemploCaracter铆sticas:
2 (binario)B  o  0b  (cero b)B 1101001d铆gitos 0 y 1
8 (octal)0  (cero)0 175d铆gitos 0 – 7
10 (decimal)160503d铆gitos 0 – 9
16 (hexadecimal)0x  (cero x)0x FF21An煤meros 0-9, letras A-F
Sistemas num茅ricos.

La caracter铆stica principal del sistema hexadecimal es que le permite escribir n煤meros decimales largos m谩s cortos, por ejemplo, un byte (255) se escribir谩 como 0xFF, dos bytes (65535) como 0xFFFF y los espeluznantes tres bytes (16.777.215) como 0xFFFFFF. No tiene idea (o ya tiene una idea) de lo conveniente y comprensible que le permite trabajar con colores y sombras.

El sistema binario se usa generalmente para la presentaci贸n visual de datos y configuraciones de bajo nivel de varios registros hardware. Por ejemplo, la configuraci贸n est谩 codificada con un byte, cada bit en ella es responsable de una configuraci贸n separada (encendido / apagado), y al transferir un byte de la forma 0b10110100, puede configurar inmediatamente un mont贸n de cosas, volveremos a esto en la lecci贸n donde trabajaremos con registros en la secci贸n de lecciones avanzadas. En la documentaci贸n sobre el microcontrolador, se describe al estilo de 鈥渆l primer bit es responsable de esto, el segundo de aquello鈥 y as铆 sucesivamente.


Variables.

Una variable es una ubicaci贸n de memoria SRAM que tiene su propio nombre 煤nico y almacena n煤meros de acuerdo con su tama帽o. Podemos referirnos a una variable por su nombre y obtener su valor o cambiarlo.

El poder del sistema binario nos persigue a煤n m谩s, porque el volumen de una celda de memoria en el microcontrolador tambi茅n es un m煤ltiplo de ella:

  • 1 byte = 8 bits = 256
  • 2 bytes = 16 bits = 65,536
  • 4 bytes = 32 bits = 4294967296

S铆, m谩s de cuatro bytes en un arduino (m谩s precisamente, en un Mc AVR ) ya no encajar谩n cuando se utilicen tipos de datos ordinarios. Se utilizan diferentes tipos de datos (variables) para trabajar con diferentes rangos de valores. De hecho, puede usar 4 bytes para almacenar cualquier cosa, pero esto no es lo 贸ptimo. Es como saber que necesitar谩s llevar un m谩ximo de 200 ml de agua (menos de 1 byte), pero igual te llevas una botella de 19 litros (2 bytes). O un vag贸n cisterna de 120 toneladas (4 bytes). Si desea escribir un c贸digo atractivo y 贸ptimo, utilice los tipos de datos oportunos. Por cierto, aqu铆 est谩n:

Tipos de datos.

NombreAlto. nombrePesoRangoCaracter铆stica
booleanbool1 byte0 o 1,  verdadero  o  falsoVariable booleana. bool en Arduino tambi茅n toma 1 byte
char1 byte-128 … 127Almacena el n煤mero de car谩cter de la tabla de caracteres ASCII
int8_t1 byte-128 … 127Tipo entero
byteuint8_t1 byte0 … 255Tipo entero
intint16_t, short2 bytes-32 768 … 32 767Tipo entero
int unsigneduint16_t, word2 bytes0 … 65 535Tipo entero
longint32_t4 bytes-2147 483 648 … 2147483647Tipo entero
unsigned longuint32_t4 bytes0 … 4 294967 295Tipo entero
float4 bytes-3.4028235E + 38 … 3.4028235E + 38Almacena n煤meros de coma flotante (decimales). Precisi贸n: 6-7 d铆gitos
double4 bytes Para AVR es lo mismo que float
int64_t8 bytes– (2 ^ 64) / 2 … (2 ^ 64) / 2-1* N煤meros muy grandes. La serie est谩ndar no puede generarlos
uint64_t8 bytes2 ^ 64-1* N煤meros muy grandes. La serie est谩ndar no puede generarlos
Tipos de datos en Arduino

* No he visto una menci贸n de esto en fuentes oficiales, pero Arduino (m谩s precisamente, el compilador) tambi茅n admite  n煤meros de 64 bits, respectivamente, el tipo de datos int64_t y uint64_t

El tama帽o m谩ximo de todos los tipos de datos se almacena en constantes y puede usarlo en su c贸digo seg煤n sea necesario:

  • UINT8_MAX – devolver谩 255
  • INT8_MAX – volver谩 127
  • UINT16_MAX – devolver谩 65 535
  • INT16_MAX – devolver谩 32 767
  • UINT32_MAX- devolver谩 4294967295
  • INT32_MAX – devolver谩 2147483647
  • UINT64_MAX – volver谩 18446744073709551615
  • INT64_MAX – devolver谩 9 223 372 036 854 775 807

Adem谩s de los tipos enteros (byte, int, long) hay m谩s interesantes:

  • bool ( booleano )  – tipo de datos booleanos, toma valores 0 y 1 o cierto y falso(verdad y mentira). En realidad se comporta como un bit, pero ocupa 8 bits. 隆Qu茅 injusticia! Hay varias formas de almacenar variables booleanas para que ocupen 1 byte, pero hablaremos de eso m谩s adelante. Tambi茅n una variable como boolean adquiere el significado cierto si le asigna un valor distinto de cero, eso es boolean a = 50;  ser谩 true y boolean b = -20; tambi茅n ser谩 true! Ejemplo: flag boolean = verdadero;
  • char– un tipo de datos de car谩cter, en equivalente num茅rico toma valores de -128 a 127. En el caso de char, estos valores son c贸digos de caracteres en la tabla de caracteres ASCII est谩ndar. Puede aceptar y almacenar datos en formato de caracteres (letra o car谩cter entre comillas simples), por ejemplo char var = ‘a’ ;
  • float – tipo de datos con punto flotante (ingl茅s float – flotante), es decir decimal.var float = 3,1415 ;

Hay varios tipos m谩s no est谩ndar que a veces se encuentran en el c贸digo de otra persona:

  • size_t- 鈥渟in贸nimo鈥 uint16_t, destinado a mostrar el tama帽o de un objeto en bytes. Por ejemplo, este tipo es devuelto por la funci贸n sizeof () y algunos otros.
  • Extendido char: char16_t, char32_t y wchar_t… Es necesario para almacenar datos de caracteres grandes para alfabetos de diferentes pa铆ses, no solo en ingl茅s.

Declarar e inicializar variables

Declaraci贸n de variable: reserve un nombre para los datos del tipo especificado. Inicializaci贸n: asignaci贸n de un valor inicial a una variable mediante un operador =

  • tipo_de_datos nombre;
  • tipo de datos nombre = valor; // declaraci贸n e inicializaci贸n
  • Tambi茅n puede聽declarar聽e聽inicializar聽varias variables separadas por comas: int a = 0, b, c = 10;
byte myVal;
int sensorRead = 10;
byte val1, val2, val3 = 10;

Puntos importantes:

  • La variable debe declararse antes de referirse a s铆 misma, literalmente debe estar m谩s alta en el c贸digo. De lo contrario, recibir谩 un error 芦variable no declarada禄 – No declarada en este 谩mbito
  • Las variables globales cuando se declaran son 0 por defecto (si no se inicializan)
  • Las variables locales (creadas dentro de funciones durante la operaci贸n del programa) pueden tener un valor aleatorio cuando se declaran, porque se asignan desde la pila. Se recomienda encarecidamente inicializarlos si en el c贸digo adicional se utilizan como un valor nulo (ejemplo a continuaci贸n)
// lee el valor del ADC
int averRead () {  
  long sum = 0; // inicializar 0
  // si esto no se hace, habr谩 problemas
  for ( int i = 0; i < 10; i ++ ) {  
   sum += analogRead(0);
    // si la suma no se inicializa a cero -
    // en lugar de la suma de valores, obtenemos basura
  }
  sum = (long)sum / 10;
  return sum;
}

Conversi贸n de tipos.

A veces es necesario convertir un tipo de datos en otro: por ejemplo, una funci贸n toma un int pero quieres darle un byte… En la mayor铆a de los casos, el compilador lo resolver谩 y convertir谩 byte en int, pero a veces se produce un error en el estilo de 禄 intentar pasar un byte a donde se espera int 芦. En este caso, puede convertir el tipo de datos, para ello basta con indicar el tipo de datos deseado entre par茅ntesis antes de la variable que se ser谩 convertida. El resultado devolver谩 una variable con un nuevo tipo de datos, pero el tipo de la variable en s铆 no cambiar谩 (funciona dentro del marco de una acci贸n). Por ejemplo:

// variable de tipo byte
byte val = 10;
// pasar a alguna funci贸n que espera un int
sendVal ( ( int ) val ) ; 

隆Y eso es! ( int ) val ser谩 manejado como int, no como byte.

A veces puede hacer la conversi贸n de tipos a trav茅s del operador de conversi贸n. Describir茅 brevemente 4 tipos principales:

  • reinterpret_cast– Tipo de fundici贸n sin comprobaci贸n, indicaci贸n directa al compilador. Se usa solo si el programador est谩 completamente seguro de sus propias acciones. No actua en const y volatile, se utiliza para convertir un puntero a un puntero, un puntero a un n煤mero entero y viceversa;
  • static_cast– convierte expresiones de un tipo est谩tico en objetos y valores de otro tipo est谩tico. Se admite la conversi贸n de tipos num茅ricos, punteros y referencias a lo largo de la jerarqu铆a de herencia tanto hacia arriba como hacia abajo. La conversi贸n se verifica a nivel de compilaci贸n y se mostrar谩 un mensaje en caso de error en la conversi贸n de tipo;
  • dynamic_cast– se utiliza para encasillamiento din谩mico en tiempo de ejecuci贸n. En el caso de un encasillado incorrecto, se lanza la excepci贸n std :: bad_cast para las referencias y se devolver谩 0 para los punteros;
  • const_cast– la conversi贸n de tipo m谩s simple. Admite const y volatile, es decir, ser constante y no optimizar la variable por parte del compilador. Esta conversi贸n se verifica a nivel de compilaci贸n y se emitir谩 un mensaje en caso de error en la conversi贸n de tipos.

C贸mo utilizar: en el ejemplo anterior;

// variable de tipo byte
byte val = 10;
// pasar a alguna funci贸n que espera un int
sendVal ( static_cast < int > ( val ) ) ;  

Constantes.

Lo que es una constante se desprende claramente de su nombre: algo, cuyo valor solo podemos leer y no podemos cambiar. Hay dos formas de establecer (declarar) una constante: Al igual que una variable, antes del tipo de datos con la palabra const. Si el valor de la variable no cambia durante la ejecuci贸n del programa, se recomienda declararlo como constante, esto permitir谩 al compilador optimizar mejor el c贸digo y en la mayor铆a de los casos ser谩 un poco m谩s f谩cil y r谩pido.

const byte myConst = 10; // declara una constante de tipo byte

Usando una directiva de preprocesador #define, que hace lo siguiente: en la etapa de compilaci贸n del c贸digo, el preprocesador reemplaza todas las secuencias de caracteres especificadas en el documento actual (recuerde que las pesta帽as de Arduino IDE son un documento) con sus valores correspondientes. Constante definida con #define no ocupa espacio en la RAM, pero se almacena como c贸digo de programa en la memoria Flash, esta es la mayor ventaja de este m茅todo. Sintaxis: #define ‘el valor del nombre’ La coma no se usa. De esta manera, generalmente se indican los pines de conexi贸n, los ajustes, varios valores, etc. Ejemplo:

#define BTN_PIN 10
#define DEFAULT_VALUE 3423

Algunas palabras m谩s sobre constantes y variables: si una variable ordinaria no cambia en ning煤n lugar durante la ejecuci贸n del programa, el compilador puede convertirla en una constante por s铆 sola y no ocupar谩 espacio en la RAM, es decir, se colocar谩 en Flash.


脕rea de visibilidad.

Las variables, constantes y otros tipos de datos (estructuras y enumeraciones) tienen un concepto tan importante como alcance. Puede ser:

  • Global
  • Local
  • Formal (par谩metro)

脕mbito Global

Una variable global se declara fuera de las funciones y est谩 disponible para leer y escribir en cualquier parte del programa, en cualquiera de sus funciones.

byte var;
void setup() {
  // cambia la variable global
  var = 50;
}
void loop() {
  // cambia la variable global
  var = 70;
}

脕mbito Local

Una variable local vive dentro de una funci贸n o dentro de cualquier bloque de c贸digo incluido entre { llaves }, disponible para leer y escribir solo en su interior. Al intentar acceder a una variable local desde otra funci贸n (fuera del { bloque } ) obtiene un error porque la variable local se vuelve a crear cuando se ejecuta el bloque de c贸digo (o funci贸n) que lo contiene, y se elimina de la memoria cuando el bloque (o funci贸n) termina de ejecutarse:

void setup() {
  byte var; // variable local para la configuraci贸n
  // cambia silenciosamente la variable local
  var = 50;
}
void loop() {
  // dar谩 como resultado un error porque var no est谩 declarado en este bloque de c贸digo
  var = 70;
  
  // haz un bloque de c贸digo separado aqu铆
  {
    byte var2 = 10;
    // 隆var2 solo existe dentro de este bloque!
  }
  // aqu铆 var2 ya se eliminar谩 de la memoria
}

Un punto importante: si el nombre de una variable local es el mismo que uno global, entonces la prioridad de llamar por nombre en la funci贸n se le da a la variable local:

void setup() {
  // pasa 10 como argumento
  myFunc ( 10 ) ;
}
void loop() {
}
void myFunc ( byte var ) {  
  // var es "local" aqu铆
  // sumar 20
  var + = 20;
  // despu茅s de} 隆la variable se eliminar谩 de la memoria!
}

Estructuras.

struct– un tipo de datos muy interesante: es una colecci贸n de variables de diferentes tipos, unidas por un nombre. En algunos casos, las estructuras pueden simplificar enormemente la escritura de c贸digo, hacerlo m谩s l贸gico y f谩cilmente modificable. La estructura del tipo de datos se declara de la siguiente manera:

struct miEstructura {
  tipo_datos nombre_variable_1;
  tipo de datos nombre_variable_2;
  tipo_datos nombre_variable_3;
} ;

La etiqueta ser谩 un nuevo tipo de datos y, con esta etiqueta, puede declarar la estructura en s铆 directamente:

struct miEstructura estructura_1;

Tambi茅n existe una variante de declarar una estructura sin crear un atajo, es decir crear una estructura sin declararla como un tipo de datos con su propio nombre.

struct { 
  tipo_datos nombre_variable_1;
  tipo_datos nombre_variable_2;
  tipo_datos nombre_variable_3;
} nombre_estructura;
  • Un miembro de estructura se direcciona de acuerdo con el siguiente esquema: nombre_estructura.nombre_variable y le permite cambiar o leer el valor.
  • Si dos estructuras tienen la misma estructura (declarada por la misma etiqueta), entonces una estructura puede simplemente equipararse a la otra, todas las variables se escribir谩n en consecuencia en sus lugares.
  • Otra opci贸n conveniente es asignar un valor como este: nombre_estructura = ( atajo ) { variable_value_1, variable_value_2, variable_value_3 } ;

Veamos un gran ejemplo que muestra todo lo anterior.

struct myStruct { // crea una estructura myStruct
  boolean a;
  byte b;
  int c;
  long d;
  byte e [ 5 ] ;
} datosA;           // e inmediatamente crea la instancia datosA
// crea una matriz de estructuras data_B de tipo myStruct
myStruct data_B [ 3 ] ;
void setup() {    
  // asigna valores a los miembros de la estructura manualmente
  datosA. a = true ;
  datosA. b = 10;
  datosA. c = 1200;
  datosA. d = 789456;
  datosA. e [ 0 ] = 10;   // 隆Tenemos una matriz!
  datosA. e [ 1 ] = 20;
  datosA. e [ 2 ] = 30;
  // asigna la estructura datosA a la estructura data_B n煤mero 0
  data_B [ 0 ] = datosA;
  // asigna un elemento de matriz de la estructura datosA 
  // estructura data_B n煤mero 1
  data_B [ 0 ] . e [ 1 ] = datosA. e [ 1 ] ;
  // completa los datos con la estructura data_B n煤mero 2
  data_B [ 2 ] = ( myStruct ) { 
    false , 30, 3200, 321654, { 1, 2, 3, 4, 5 }
  } ;
}

驴Para qu茅 sirven las estructuras? La mayor铆a de los ejemplos en Internet utilizan estructuras para almacenar datos de direcciones, p. Ej. creando una base de datos de direcciones: nombre, apellidos, n煤mero de tel茅fono, etc. En mi pr谩ctica, las estructuras resultaron ser muy adecuadas para crear un men煤 con una gran cantidad de modos y configuraciones (digamos varios canales, cada uno con el mismo conjunto de configuraciones). Es muy conveniente transmitir y recibir estructuras usando m贸dulos RF24, es decir en lugar de matrices, es m谩s conveniente utilizar estructuras y transferir varios tipos de datos en una l铆nea a la vez. Adem谩s, toda la estructura se puede escribir en la eeprom en una l铆nea (con el comando put) y leerla en una l铆nea desde all铆, sin molestarse con los n煤meros de celda, como sucede cuando se escriben datos manualmente.

Tama帽o del elemento de estructura

Las estructuras te permiten hacer algo muy interesante para la optimizaci贸n de la memoria: especificar el peso m谩ximo de un elemento en bits. De esta manera, incluso puede crear indicadores de un solo bit (bool/boolean ocupa 8 bits en memoria). Esto se hace usando el operador de dos puntos. :

// crear y empaquetar la estructura de banderas de un bit
struct MyFlags {
  bool button: 1;
  bool state: 1;
  bool position: 1;
  bool flag_3: 1;
  bool flag_4: 1;
};
// declaramos una estructura de tipo MyFlags
MyFlags flags;
void setup() {
  Serial.begin(9600);
  // por defecto todas las banderas son false
  // hacer referencia a una estructura
  // podemos cambiar y trabajar como con banderas ordinarias
  Serial.println(flags.button);
  Serial.println(flags.state);
  flags.position = true;
  Serial.println(flags.position);
}
void loop() {}

De la misma forma, puedes empaquetar tipos de datos enteros, por ejemplo, sabemos que el valor de una variable no exceder谩 de 50, podemos declararlo dentro de la estructura como byte val: 6 lo que lo convertir谩 en 6 bits en la memoria. No tiene sentido para un peque帽o conjunto de variables, pero si realmente hay muchas, 隆vale la pena empaquetarlas en una estructura!

Estructuras anidadas

Las estructuras tambi茅n se pueden anidar entre s铆, el acceso al elemento deseado tambi茅n se realiza mediante el operador 芦punto禄, un ejemplo simple:

struct Values {
  int value1;
  float value2;
};
struct BigStruct {
  Values values; 
  int otherValue;
};
BigStruct myStruct;
myStruct.values.value2 = 3.14;

Enumeraciones.

Enumeraciones (enum – enumeraci贸n): un tipo de datos, que es un conjunto de constantes con nombre, se necesita principalmente para la conveniencia del programador. Solo un ejemplo de la experiencia: digamos que tenemos un modo variable, que es responsable del n煤mero de modo del dispositivo. Recordamos por nosotros mismos a qu茅 valor de la variable corresponder谩 el modo, y en alg煤n lugar lo escribimos, por ejemplo 0 – modo normal, 1 – modo de espera, 2 – modo de configuraci贸n_1, 3 – modo de configuraci贸n_2, 4 – calibraci贸n, 5 – modo de emergencia, error. Al escribir o leer un programa, a menudo tendr谩 que consultar esta lista para evitar confusiones. Puede dar el primer paso en la optimizaci贸n: llame a cada modo con una definici贸n:

#define NORMAL 0
#define WAITING 1
#define SETTINGS_1 2
#define SETTINGS_2 3
#define CALIBRATION 4
#define ERROR_MODE 5

Por lo tanto, en lugar de n煤meros, ser谩 posible usar palabras comprensibles y ser谩 mucho m谩s f谩cil navegar por el c贸digo. Utilizando enum simplifica un poco m谩s esta construcci贸n: la enumeraci贸n le permite crear una variable (por defecto de tipo int), que puede aceptar s贸lo los 芦nombres禄 que se le especifican. Esto es conveniente porque un programa puede contener diferentes guardianes de modo con el mismo nombre y a diferencia #define esto no dar谩 lugar a errores.

Declarar una enumeraci贸n es algo similar a declarar una estructura:

enum shortcut { nombre1, nombre2, nombre3, nombre4, nombre5 } ;

As铆 es como declaramos el atajo. Ahora, usando este atajo, podemos declarar la enumeraci贸n en s铆:

atajo shortcut;

Al igual que las estructuras, puede declarar una enumeraci贸n sin crear un atajo (驴por qu茅 necesitamos una l铆nea adicional?):

enum { nombre1, nombre2, nombre3, nombre4, nombre5 } nombre de enumeraci贸n; 

Una enumeraci贸n (enum) es un tipo definido con constante de tipo entero. En la declaraci贸n de un tipo enum creamos una lista de tipo de datos que se asocian con las constantes enteras 0, 1, 2, 3, 4, 5…

su forma de definirlas es la siguiente:

enum 
{
	enumerador1, enumerador2, 鈥 enumeradorn
};

enum Nombre
{
	enumerador1, enumerador2, 鈥 enumeradorn
};

En este caso al ser declaradas enumerador1 toma el valor entero de 0, enumerador2 el valor de 1 y asi sucesivamente para cada una de las expresiones siguientes.

Al declarar la enum se puede asociar a los tipos de datos a valores constantes en vez de la asociaci贸n que por defecto se realiza (0, 1, 2, 鈥), se utiliza entonces este formato:

enum Nombre
{
	enumerador1 = valor_constante1,
	enumerador2 = valor_constante2,
	...
	enumeradorn = valor_constanten,
};

Un ejemplo de una enumeracion:

{
	FALSE,
	TRUE
};

A los enumeradores se pueden asignar valores o expresiones constantes durante la declaraci贸n:

enum Hexaedro
{
	VERTICE = 8,
	LADOS = 12,
	CARAS = 6
};

Tipos personalizados, typedef.

El idioma tiene una herramienta typedef, lo que le permite crear su propio tipo de datos basado en otro est谩ndar. 驴Para qu茅? Por un lado, para la conveniencia del programador, por otro lado, para confundirlo =) Nunca lo he usado, 隆pero necesitas saber esto para analizar los c贸digos de otras personas de Internet! Entonces, typedef funciona as铆: typedef nombre del tipo; – crear un nuevo tipo de datos basado en el nombre del tipo… Ejemplo:

ypedef byte color;

Crea un tipo de datos llamado color que ser谩 absolutamente id茅ntico al tipo byte (es decir, tome 0-255). Ahora puedes crear variables con este tipo:

color R, G, B;

Espacio de nombres.

Considere la siguiente situaci贸n, cuando tenemos dos personas con el mismo nombre, Sonia, en la misma clase. Siempre que necesitemos diferenciarlos definitivamente tendr铆amos que utilizar alg煤n tipo de informaci贸n adicional junto con su nombre, como la zona, si viven en otra zona o el nombre de su madre o padre, etc.

La misma situaci贸n puede surgir en sus programas C ++. Por ejemplo, podr铆a estar escribiendo un c贸digo que tiene una funci贸n llamada xyz () y hay otra biblioteca disponible que tambi茅n tiene la misma funci贸n xyz (). Ahora el compilador no tiene forma de saber a qu茅 versi贸n de la funci贸n xyz () se est谩 refiriendo dentro de su c贸digo.

Un espacio de nombres est谩 dise帽ado para superar esta traba y se utiliza como informaci贸n adicional para diferenciar funciones, clases, variables, etc. similares con el mismo nombre disponible en diferentes bibliotecas. Usando el espacio de nombres, puede definir el contexto en el que se definen los nombres. En esencia, un espacio de nombres define un 谩mbito.

Definici贸n de un espacio de nombres

Una definici贸n de espacio de nombres comienza con la palabra clave namespace seguida del nombre del espacio de nombres de la siguiente manera:

namespace namespace_name {
   // code declarations
}

Para llamar a la versi贸n habilitada para el espacio de nombres de una funci贸n o variable, anteponga (: 馃檪 a el nombre del espacio de nombres de la siguiente manera:

name::code;  // code could be variable or function.

Veremos c贸mo el espacio de nombres abarca las entidades, incluidas las variables y las funciones:

#include <iostream>
using namespace std;

// primer espacio de nombres
namespace first_space {
   void func() {
      cout << "Inside first_space" << endl;
   }
}

// segundo espacio de nombres
namespace second_space {
   void func() {
      cout << "Inside second_space" << endl;
   }
}

int main () {
   // Calls function from first name space.
   first_space::func();
   
   // Calls function from second name space.
   second_space::func(); 

   return 0;
}

Si compilamos y ejecutamos el c贸digo anterior, esto producir铆a el siguiente resultado:

Inside first_space
Inside second_space

La directiva using

Tambi茅n puede evitar anteponer espacios de nombres con la directiva using namespace. Esta directiva le dice al compilador que el c贸digo subsiguiente utiliza nombres en el espacio de nombres especificado. Por lo tanto, el espacio de nombres est谩 impl铆cito para el siguiente c贸digo:

#include <iostream>
using namespace std;

// first name space
namespace first_space {
   void func() {
      cout << "Inside first_space" << endl;
   }
}

// second name space
namespace second_space {
   void func() {
      cout << "Inside second_space" << endl;
   }
}

using namespace first_space;
int main () {
   // This calls function from first name space.
   func();
   
   return 0;
}

Si compilamos y ejecutamos el c贸digo anterior, esto producir铆a el siguiente resultado:

Inside first_space

La directiva ‘using’ tambi茅n se puede usar para referirse a un elemento en particular dentro de un espacio de nombres. Por ejemplo, si la 煤nica parte del espacio de nombres est谩ndar que pretende utilizar es cout, puede hacer referencia a 茅l de la siguiente manera:

using std::cout;

Especificadores.

Adem谩s de poder convertir una variable en una constante usando el especificador const tenemos algunas herramientas m谩s interesantes para trabajar con una variable.

Static

est谩tic- hace que la variable (o constante) sea est谩tica. Qu茅 significa eso?

Local static

Primero, recordemos c贸mo funciona una variable local ordinaria: cuando se llama a una funci贸n, la variable local inicializa y obtiene un valor cero, a menos que se especifique lo contrario. Si la variable local se declara como est谩tic – almacenar谩 su valor de una llamada a otra llamada de funci贸n, es decir, se convertir谩, en t茅rminos generales, en globalmente local.

Global static

Una variable global est谩tic pasa a estar disponible solo en este archivo, el especificador est谩tic le permite ocultarlo de las influencias de otros archivos de programa.

Extern

extern– indica al compilador que la variable est谩 declarada en alg煤n lugar de otro archivo de programa, y 鈥嬧媎urante la compilaci贸n la encontrar谩 y la usar谩. Y si no lo encuentra, no habr谩 error. Por ejemplo, con este c贸digo, puede restablecer el contador millis ().

// indicamos lo que queremos usar
// variable timer0_millis,
// que se declara en alg煤n lugar lejano
// en archivos Arduino
extern volatile unsigned long timer0_millis;   
void setup() {
  timer0_millis = 0;  
}
void loop() {
  
}

Volaltile

vol谩tile– este especificador le dice al compilador que esta variable no necesita ser optimizada y su valor se puede cambiar desde alg煤n lugar externo.聽Las variables con este especificador se usan generalmente en manejadores de interrupciones.聽Los c谩lculos con tales variables tampoco est谩n optimizados y requieren m谩s tiempo de procesamiento.


Deja un comentario