domingo, 9 de agosto de 2015

[Lenguaje Arduino 04] Variables (Parte 4: Tipos de variables)

Hola gente en la publicación de hoy les traigo Variables (Parte 2: Tipos de variables), como dice el nombre, estudiaremos los tipos de variables que existen en arduino y como se utilizan cuando uno quiere programar, ademas de esto se proporciona información importantes y practica que nos sirve para la programación, como cuanto espacio usa cada variable en nuestro código final, espero que les guste.

Tipos posibles de una variable
Los tipos de variables que el lenguaje Arduino admite son:
  1. boolean
  2. char
  3. byte
  4. int
  5. word
  6. short
  7. long
  8. unsigned long
  9. float
  10. double
  11. array
1.-Tipo “boolean” (boleano): las variables de este tipo solo pueden tener dos valores: verdadero o falso. Se utilizan para almacenar un estado de entre esos dos posibles, y así hacer que el skech reaccione según detecte en ellas uno u otro. Por ejemplo, las variables booleanas se pueden usar para comprobar si se han recibido datos de un sensor (verdadero) o no (falso), para comprobar si algún actuador está disponible (verdadero) o no (falso), para comprobar si el valor de otra variable diferente cumple una determinada condición como por ejemplo la de ser mayor que un número concreto (verdadero) o no (falso). El valor guardado en una variable booleana ocupa siempre un byte de memoria.

Para asignar explícitamente a una variable de tipo “boolean” el valor de verdadero, se puede utilizar la palabra “true” (sin comillas) o bien el valor “1” (sin comillas), y para asignarle el valor de falso se puede utilizar la palabra “false” (sin comillas) o bien el valor “0” (sin comillas). Es decir, si en nuestro sketch de ejemplo la variable “mivariable” hubiera sido de tipo “boolean” en vez de “int”, para asignarle el valor de “true” tendríamos que haber escrito una línea similar a boolean mivariable=true; o bien boolean mivariable=1; (en realidad, una variable booleana con un valor cualquiera diferente de 0 ya se interpreta que tiene un valor verdadero: no tiene por qué ser el valor 1 concretamente).

2.-Tipo “char” (character): el valor que puede tener una variable de este tipo es siempre un solo carácter (una letra, un dígito, un signo de puntuación...). Si lo que queremos es almacenar una cadena de caracteres (es decir, una palabra o una frase) el tipo “char” no nos sirve, deberemos usar otro tipo explicado posteriormente.
Para asignar explícitamente a una variable de tipo “char” un determinado valor (es decir, un carácter), deberemos tener la precaución de escribir ese carácter entre comillas simples. Por tanto, si en el sketch del ejemplo [Lenguaje Arduino 01] la variable “mivariable” hubiera sido de tipo “char” en vez de “int”, para asignarle el valor de la letra A tendríamos que haber escrito una línea similar a char mivariable='A';.

En realidad, los caracteres se almacenan internamente como números ya que los dispositivos electrónicos son incapaces de trabajar con “letras” directamente: las han de “traducir” siempre primero a números para entonces poderlas almacenar y procesar. Para saber a qué número interno corresponde un determinado carácter, y viceversa, la placa Arduino utiliza la llamada tabla ASCII, la cual es una simple lista de equivalencias que asocia cada carácter con un número determinado.

El hecho de que los caracteres para la placa Arduino en realidad sean reconocidos como números permite que sea posible realizar operaciones aritméticas con esos caracteres (o mejor dicho, con su valor numérico correspondiente dentro de la tabla ASCII). Por ejemplo: si realizamos la operación 'A' + 1 obtendríamos el valor 66, ya que en la tabla ASCII el valor numérico del carácter 'A' es 65. De hecho, incluso podríamos inicializar una variable “char” asignándole un valor numérico en vez del carácter correspondiente (es decir: la línea char mivariable='A'; la podríamos escribir como char mivariable=65; y ambas serían equivalentes).

Cada variable de tipo carácter ocupa 8 bits (un byte) de memoria para almacenar su valor. Esto implica que existen 28 = 256 valores diferentes posibles para una variable de este tipo (es fácil ver esto si se cuentan las combinaciones de 0s y 1s que se pueden obtener con 8 posiciones). Como los valores de tipo “char” en realidad son números, los valores que puede almacenar una variable de este tipo están dentro un rango que va desde el número -128 hasta el 127.

3.-Tipo “byte”: el valor que puede tener una variable de este tipo es siempre un número entero entre 0 y 255. Al igual que las variables de tipo “char”, las de tipo “byte” utilizan un byte (8 bits) para almacenar su valor y, por tanto, tienen el mismo número de combinaciones numéricas posibles diferentes (256), pero a diferencia de aquellas, los valores de una variable “byte” no pueden ser negativos.

4.-Tipo “int” (entero): el valor que puede tener una variable de este tipo es un número entero entre -32768 (-215) y 32767 (215-1), gracias a que utilizan 2 bytes (16 bits) de memoria para almacenarse. Esto es así para todas las placas Arduino excepto para la Due: en este modelo de placa el tipo “int” utiliza 4 bytes, y por tanto, su valor puede estar dentro de un rango mayor, concretamente entre -2,147,483,648 (-231) y 2,147,483,647 (231 -1) .

5.-Tipo “word”: las variables de tipo “word” en la placa Arduino Due ocupan 4 bytes para almacenar su valor. Por tanto, tienen el mismo número de combinaciones numéricas posibles diferentes que las variables “int”, pero a diferencia de estas, los valores de una variable “word” no pueden ser negativos. En las placas basadas en microcontroladores de tipo AVR ocurre lo mismo: las variables de tipo “int” y “word” ocupan el mismo espacio de memoria (aunque en este caso, no obstante, solamente son 2 bytes) pero los valores de las segundas no pueden ser negativos. Es fácil ver que el valor que puede tener una variable “word” en todas las placas excepto la Arduino Due es un número entero entre 0 y 65535 (216-1).

6.-El tipo “short”: el valor que puede tener una variable de este tipo para todos los modelos de placa (ya sean basadas en microcontroladores de tipo AVR –la mayoría– o de tipo ARM –la Due–) es un número entero entre -32768 (-215) y 32767 (215-1), gracias a que utilizan 2 bytes (16 bits) de memoria para almacenarse. En este sentido, los tipos “short” e “int” para placas de la familia AVR son equivalentes, pero para la placa Arduino Due el tipo “short” es el único que utiliza 16 bits (2 bytes).

7.-Tipo “long”: el valor que puede tener una variable de este tipo para todos los modelos de placa (ya sean basadas en microcontroladores de tipo AVR o de tipo ARM) es un número entero entre -2.147.483.648 y 2.147.483.647 gracias a que utilizan 4 bytes (32 bits) de memoria para almacenarse. En este sentido, los tipos “long” e “int” para placas de la familia ARM son equivalentes.

8.-Tipo “unsigned long”: el valor que puede tener una variable de este tipo para todos los modelos de placa (ya sean basadas en microcontroladores de tipo AVR o ARM) es un número entero entre 0 y 4.294.967.295 (232-1). Al igual que las variables de tipo “long”, las de tipo “unsigned long” utilizan 4 bytes (32 bits) para almacenar su valor, y por tanto, tienen el mismo número de combinaciones numéricas posibles diferentes (232), pero a diferencia de aquellas, los valores de una variable “unsigned long” no pueden ser negativos (tal como ya indica su propio nombre). En este sentido, los tipos “unsigned long” y “word” para placas de la familia ARM son equivalentes.


En los párrafos anteriores se ha comentado, en referencia a las variables numéricas, que todas ellas tienen un rango de valores válido, y por tanto, asignar un valor fuera de este puede tener consecuencias inesperadas. Esto hay que tenerlo en cuenta para saber elegir correctamente el tipo de variable numérica que necesitemos. En el caso de las variables enteras, lo que uno podría pensar es utilizar siempre las de tipo “unsigned long”, las cuales admiten el rango más elevado. Pero esto no es buena idea, porque cada variable de este tipo ocupa cuatro veces más que una variable “byte”, y si (por ejemplo) sabemos de antemano que los valores a almacenar no serán mayores de 100, sería una completa pérdida de memoria el utilizar variables “unsigned long”. Y la memoria del microcontrolador es uno de los recursos más preciados y escasos para irlo desaprovechando por una mala elección de tipos.

En el supuesto caso de que un valor supere el rango válido (tanto por “encima” como por “debajo”), lo que ocurrirá es que “dará la vuelta”, y el valor continuará por el otro extremo. Es decir, si tenemos una variable de tipo “byte” (por ejemplo) cuyo valor es actualmente 255 y se le suma 1, su nuevo valor será entonces 0 (si se le sumara 2, valdría entonces 1, y así). Lo mismo pasa si se supera el límite inferior: si tenemos una variable de tipo “byte” (por ejemplo) cuyo valor actual es 0 y se le resta 1, su nuevo valor será entonces 255 (si se le restara 2, valdría entonces 254, y así). De hecho, esto lo podemos observar si ejecutamos el sketch de ejemplo [Lenguaje Arduino 01] y esperamos lo suficiente: cuando los valores de la variable “mivariable” (de tipo “int”, recordemos) lleguen a su límite superior (32767), veremos cómo automáticamente el siguiente valor vuelve a ser el del principio de su rango (concretamente el -32768) para seguir aumentando sin parar otra vez hasta llegar al máximo y volver a caer de nuevo al mínimo, y así. Este fenómeno se llama “overflow”.


Existen más tipos de datos admitidos por el lenguaje Arduino además de los ya comentados:

9.-Tipo “float”: el valor que puede tener una variable de este tipo es un número decimal. Los valores “float” posibles pueden ir desde el número -3,4028235·1038 hasta el número 3,4028235·1038. Debido a su grandísimo rango de valores posibles, los números decimales son usados frecuentemente para aproximar valores analógicos continuos. No obstante, solo tienen 6 o 7 dígitos en total de precisión. Es decir, los valores “float” no son exactos, y pueden producir resultados inesperados, como por ejemplo que, 6.0/3.0 no dé exactamente 2.0.

Otro inconveniente de los valores de tipo “float” es que el cálculo matemático con ellos es mucho más lento que con valores enteros, por lo que debería evitarse el uso de valores “float” en partes de nuestro sketch que necesiten ejecutarse a gran velocidad.

Los números decimales se han de escribir en nuestro sketch utilizando la notación anglosajona (es decir, utilizando el punto decimal en vez de la coma). Si lo deseamos, también se puede utilizar la notación científica (es decir, el número 0,0234 –equivalente a 2,34·10-2 – lo podríamos escribir por ejemplo como 2.34e-2).

10.-Tipo “double”: Es un sinónimo exactamente equivalente del tipo “float”, y por tanto no aporta ningún aumento de precisión respecto a este (a diferencia de lo que pasa en otros lenguajes, donde “double” sí que aporta el doble de precisión). Tanto una variable de tipo “double” como una de tipo “float” ocupan cuatro bytes de memoria.

11.-Tipo “array” (vectores): este tipo de datos en realidad no existe como tal. Lo que existen son arrays de variables de tipo “boolean”, arrays de variables de tipo “int”, arrays de variables de tipo “float”, etc. En definitiva: arrays de variables de cualquier tipo de los mencionados hasta ahora. Un array (también llamado “vector”) es una colección de variables de un tipo concreto que tienen todas el mismo y único nombre, pero que pueden distinguirse entre sí por un número a modo de índice. Es decir: en vez de tener diferentes variables –por ejemplo de tipo “char” – cada una independiente de las demás (varChar1, varChar2,varChar3...) podemos tener un único array (vector) que las agrupe todas bajo un mismo nombre (por ejemplo, varChar), y que permita que cada variable pueda manipularse por separado gracias a que dentro del array cada una está identificada mediante un índice numérico, escrito entre corchetes (varChar[0], varChar[1], varChar[2]...). Los arrays sirven para ganar claridad y simplicidad en el código, además de facilitar la programación.

Podemos crear –declarar– un array (ya sea en la zona de declaraciones globales o bien dentro de alguna sección concreta), de las siguientes maneras:

int varInt[6]; 
Declara un array de 6 elementos (es decir, variables individuales) sin inicializar ninguno.

int varInt[] = {2,5,6,7};
Declara un array sin especificar el número de elementos. No obstante, se asignan (entre llaves, separados por comas) los valores directamente a los elementos individuales, por lo que el compilador es capaz de deducir el número de elementos total del array (en el ejemplo, cuatro).

int varInt[8] = {2,5,6,7};
Declara un array de 8 elementos e inicializa algunos de ellos (los cuatro primeros), dejando el resto sin inicializar. Lógicamente, si se inicializaran más elementos que lo que permite el tamaño del array (por ejemplo, si se asignan 9 valores a un array de 8 elementos), se produciría un error.

char varChar[6] = “hola”;
char varChar[6] = {'h','o','l','a'};
char varChar[] =”hola”;
La primera forma declara e inicializa un array de seis elementos de tipo “char”. Como los arrays de tipo “char” son en realidad cadenas de caracteres (es decir: palabras o frases, “strings” en inglés) tienen la particularidad de poder inicializarse tal como se muestra en la primera forma: indicando directamente la palabra o frase escrita entre comillas dobles. Pero también se pueden declarar como un array “estándar”, que es como muestra la segunda forma. Observar en este caso la diferencia de comillas: un carácter individual siempre se especifica entre comillas simples, y el valor de una cadena siempre se especifica entre comillas dobles. También es posible, tal como muestra la tercera forma, declarar una cadena sin necesidad de especificar su tamaño (ya que el compilador lo puede deducir a partir del número de elementos – es decir, caracteres– inicializados).

Hay que tener en cuenta que el primer valor del array tiene el índice 0 y por tanto, el último valor tendrá un índice igual al número de elementos del array menos uno (n-1). Cuidado con esto, porque asignar valores más allá del número de elementos declarados del array es un error. En concreto, si por ejemplo tenemos un array de 2 elementos de tipo entero (es decir, declarado así: int varInt[2]; ), para asignar un nuevo valor (por ejemplo, 27) a su primer elemento deberíamos escribir: varInt[0] = 27; , y para asignar el segundo valor (por ejemplo, 17), escribiríamos varInt[1] = 17; . Pero si asignáramos además un tercer valor así, varInt[2] = 53; , cometeríamos un error porque estaríamos sobrepasando el final previsto del array (y por tanto, utilizando una zona de la memoria no reservada, con resultados imprevisibles).

Por otro lado, además de asignar a un elemento de un array un valor explícito tal como se acaba de comentar en el párrafo anterior, también es posible asignar a un elemento de un array el valor que tenga en ese momento otra variable independiente (preferiblemente del mismo tipo). Por ejemplo, mediante la línea varInt[4]=x; estaremos asignando el valor actual de una variable llamada “x” al quinto elemento (¡el índice empieza por 0!) del array llamado “varInt”. Y a la inversa también es posible: para asignar el valor que tenga en ese momento el quinto elemento del array “varInt” a la variable independiente “x”, simplemente deberemos ejecutar la línea: x=varInt[4];

En el caso concreto de los arrays de caracteres (las “cadenas” o “strings”), hay que tener en cuenta una particularidad muy importante: este tipo de arrays siempre deben ser declarados con un número de elementos una unidad mayor que el número máximo de caracteres que preveamos guardar. Es decir, si se va a almacenar la palabra “hola” (de cuatro letras), el array deberá ser declarado como mínimo de 5 elementos. Esto es así porque este último elemento siempre se utiliza para almacenar automáticamente un carácter especial (el carácter “nulo”, con código ASCII 0), que sirve para marcar el final de la cadena. Esta marca es necesaria para que el compilador sepa que la cadena ya terminó y no intente seguir leyendo más posiciones. Si no sabemos de antemano cuál es la longitud del texto que se guardará en un array de caracteres, podemos declarar este con un número de elementos lo suficientemente grande como para que haya elementos sin ser asignados, aun sabiendo que así posiblemente estaremos desaprovechando memoria del microcontrolador.

Finalmente, comentar que a menudo es conveniente, cuando se trabaja con grandes cantidades de texto (por ejemplo, en un proyecto con pantallas LCD), utilizar arrays de strings. Para declarar un array de esta clase, se utiliza el tipo de datos especial “char*” (notar el asterisco final). Un ejemplo de declaración e inicialización de este tipo sería: char* varCadenas[]={"Cad0","Cad1","Cad2","Cad3"};.

En realidad, el asterisco en la declaración anterior indica que en realidad estamos declarando un array de “punteros”, ya que para el lenguaje Arduino, las cadenas son punteros. Los “punteros” son unos elementos del lenguaje Arduino (provenientes del lenguaje C en el que está basado) muy potentes pero a la vez ciertamente complejos. Aqui no se tratarán, debido a que sus posibles usos son avanzados y pueden confundira quienes se inician en la programación: lo único que necesitaremos saber es cómo se declaran los arrays de cadenas y nada más. La buena noticia es que, una vez declarado el array de cadenas, podemos trabajar con él (asignando valores a sus elementos, consultándolos, etc.) como cualquier otro tipo de array sin notar para nada que estamos usando punteros.

Si te gusto esta publicación comparte para que siga haciendo mas tutoriales. En el siguiente enlace puedes visitar todos los tutoriales de la sección [Lenguaje Arduino] Y si tienes alguna duda o comentario no dudes en realizarlo por medio de este mismo blog :)

2 comentarios:

  1. Muy Buenas, Se puede asignar un array a otro directamente, sin tener que hacerlo elemento por elemento.

    ResponderBorrar
  2. Muy Buenas, Se puede asignar un array a otro directamente, sin tener que hacerlo elemento por elemento.

    ResponderBorrar