Diferencia entre revisiones de «asterisco»
(→Vea también) |
(→Ejemplo 2) |
||
(No se muestran 21 ediciones intermedias de 2 usuarios) | |||
Línea 1: | Línea 1: | ||
== Descripción == | == Descripción == | ||
− | + | El operador de '''asterisco''' <nowiki>(*)</nowiki> tiene dos funciones: | |
+ | |||
+ | # Declarar variables como punteros de cierto tipo de dato. | ||
+ | # Desreferenciar; dicho en otras palabras, trabajar con el valor apuntado en vez del puntero en sí. Dicho valor dependerá del tipo de dato del puntero (es "interpretado" segun la posición de memoria dada). | ||
+ | |||
+ | {{Nota|A menos que el microcontrolador tenga más de 32 KB de [[SRAM]], los punteros siempre serán variables enteras de 16 bits (el equivalente a [[word]]) independientemente del tipo que interpreten. De hecho también se pueden realizar las típicas operaciones matemáticas en punteros como cualquier otro tipo número-entero de variable conocido.}} | ||
== Sintaxis == | == Sintaxis == | ||
+ | Declara un puntero. La diferencia con la declaracion de una variable normal radica en el * después del tipo de dato y antes del nombre. | ||
+ | <pre> | ||
+ | tipo* puntero; | ||
+ | </pre> | ||
+ | |||
+ | Asigne el valor apuntado por variable2. Cuando no es en una declaración, se usa para desreferenciar. El * va antes del nombre del puntero. | ||
+ | <pre> | ||
+ | variable1 = *variable2; | ||
+ | </pre> | ||
== Parámetros == | == Parámetros == | ||
+ | ;puntero: Una variable que se utiliza como puntero, y que la posición apuntada será interpretada como si fuera un dato de tipo '''tipo'''. | ||
+ | ;variable1: Una variable normal (que solamente almacena un dato del tipo declarado). | ||
+ | ;<nowiki>*</nowiki>variable2: Puntero del cuál se quiere obtener el valor de la posición a la que apunta. | ||
+ | |||
+ | == Comentarios == | ||
+ | Los punteros son uno de los temas más complicados para los principiantes de Arduino y es posible escribir la gran mayoría de [[boceto]]s sin tener que encontrarse con los punteros. Sin embargo, para la manipulación de ciertas estructuras de datos, el uso de punteros puede simplificar el código, y el conocimiento de la manipulación de punteros es útil para tener en la propia caja de herramientas. | ||
+ | |||
+ | Bien usados, los punteros son excepcionalmente útiles para resolver cierto tipo de problemas, pero aprender a manejarlos puede provocar serios dolores de cabeza, especialmente cuando tratas de depurar un programa que se niega a funcionar como debe. | ||
+ | |||
+ | En primer lugar debemos entender que la memoria del Arduino está numerada en posiciones. Cada posición de memoria tiene una dirección única, que debe ser especificada cuando queremos leer o escribir su valor. Si miramos el tipo de memoria de los distintos Arduinos, vemos que, por ejemplo, el [[UNO]] dispone de 32 K de memoria [[flash]] para almacenamiento de programas y de 2 K de [[SRAM]] para almacenar de variables. | ||
+ | |||
+ | Cuando definimos una variable en el [[boceto]], el compilador le asigna una posición en la memoria [[SRAM]]. Si la variables es del tipo [[char]] o [[byte]] asigna un byte de memoria, si es del tipo [[int]] o [[unsigned int]] le asigna dos posiciones de memoria y si es un tipo [[long]] o [[unsigned long]] le asigna 4 posiciones de memoria. | ||
+ | |||
+ | <pre> | ||
+ | int numero; //Declaramos variable | ||
+ | numero = 123; //Definimos variable | ||
+ | </pre> | ||
+ | |||
+ | {|class="wikitable" | ||
+ | !Nombre!!Dirección de memoria!!Contenido | ||
+ | |- | ||
+ | |numero||2050||123 | ||
+ | |} | ||
+ | |||
+ | Esto nos da una idea de lo que puede suceder si escribimos un valor long en una dirección de memoria que corresponde a un int. Como el tipo long ocupa 4 bytes, cuando intentemos meterlos en una dirección a la que se ha asignado 2 bytes, va a ocupar el contenido de las siguientes posiciones de memoria, que pueden estar usadas por otros satos. Prueba esto: | ||
+ | |||
+ | <pre> | ||
+ | void setup(){ | ||
+ | Serial.begin(115200); | ||
+ | int numero; | ||
+ | long n = 100000; | ||
+ | numero = n; //Estas intentando guardar 4 byte donde solo reservaste 2 bytes | ||
+ | Serial.println(numero); //-31072 | ||
+ | } | ||
+ | void loop(){ | ||
+ | //Nada | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | Cuando declaras una matriz, en realidad estás declarando un puntero. De hecho es posible utilizar '''[]''' en cualquier puntero, aunque este no se haya declarado explícitamente como una matriz.<br> | ||
+ | La diferencia entre declarar un puntero y una matriz, está en lo que ocurre implícitamente: declarar un puntero es igual que declarar cualquier otra variable simple; mientras que declarar una matriz es más que asignar 2 bytes, es asignar otro espacio según el tipo de dato y el número ingresado entre los '''[]''' (o en su defecto, la cantidad de elementos que aparece entre los '''{}'''), inicializarlo con ceros o con los valores preestablecidos, y finalmente asignarle al respectivo puntero la posición del primer byte de ese espacio que tuvo que asignar. | ||
+ | |||
+ | El acceso a un elemento es más amigable hacerlo con los '''[]''', sin embargo existen dos formas de realizar la misma acción: | ||
+ | <pre> | ||
+ | matriz[i] = 4; | ||
+ | *(matriz + i) = 4; | ||
+ | // Ambos hacen exactamente lo mismo | ||
+ | </pre> | ||
+ | De las dos formas es válido ya que una matriz es un puntero, y en cierto modo también se puede decir que al revés. | ||
== Advertencias == | == Advertencias == | ||
+ | * Errores de '''overflow''' de registro no son advertidos por el compilador, ya que el puntero puede apuntar en cualquier posición arbitraria de la memoria. Tanto así que puede apuntarse a sí mismo, a otro puntero, e incluso a cualquier variable, matriz u objeto existente en el momento. | ||
+ | * '''Un gran poder conlleva una gran responsabilidad'''. Debido a que el puntero tiene el "superpoder" de meterse en cualquier parte de la memoria de ejecución ([[SRAM]]), existe el riesgo de corromperla sin previo aviso y colgar el programa en cuestión. | ||
− | == Ejemplo == | + | == Ejemplo 1 == |
− | < | + | <syntaxhighlight lang="c++"> |
byte *p; i=5, resultado; | byte *p; i=5, resultado; | ||
p = &i; //5 | p = &i; //5 | ||
resultado = *p; //resultado toma el valor de la posicion de memoria de p | resultado = *p; //resultado toma el valor de la posicion de memoria de p | ||
− | </ | + | </syntaxhighlight> |
+ | |||
+ | == Ejemplo 2 == | ||
+ | Implementamos un '''buffer circular''' con puntero. | ||
+ | |||
+ | <syntaxhighlight lang="c++"> | ||
+ | const int ventana = 5; | ||
+ | int circularBuffer[ventana]; | ||
+ | int* indice = circularBuffer; | ||
+ | |||
+ | void meter(int valor){ | ||
+ | *indice = valor; | ||
+ | indice++; | ||
+ | if (indice >= circularBuffer + ventana){ | ||
+ | indice = circularBuffer; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void setup(){ | ||
+ | Serial.begin(115200); | ||
+ | } | ||
+ | |||
+ | void loop(){ | ||
+ | if (Serial.available()){ | ||
+ | int x = Serial.parseInt(); | ||
+ | meter(x); | ||
+ | for (byte n=0; n<ventana; n++){ | ||
+ | Serial.print(circularBuffer[n]); | ||
+ | Serial.print(","); | ||
+ | } | ||
+ | Serial.println(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Ejemplo 3 == | ||
+ | No se puede crear una [[matriz]] de matrices. Pero si se puede usar '''punteros''' a matrices, asi: | ||
+ | |||
+ | <syntaxhighlight lang="c++"> | ||
+ | void setup(){ | ||
+ | Serial.begin(9600); | ||
+ | byte cero[] = { 0xA6, 0xD4, 0xAC, 0xF8 }; | ||
+ | byte uno[] = { 0xE9, 0x48, 0xCC, 0xE3 }; | ||
+ | byte dos[] = { 0x46, 0x6F, 0xA5, 0xF8 }; | ||
+ | byte tres[] = { 0xC2, 0x9D, 0xFC, 0x33 }; | ||
+ | byte* matriz[4] = {cero, uno, dos, tres}; //Usando puntero | ||
+ | for (byte i=0; i<4; i++){ | ||
+ | for (byte j=0; j<4; j++){ | ||
+ | Serial.print(matriz[i][j], HEX); | ||
+ | Serial.print(", "); | ||
+ | } | ||
+ | Serial.println(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void loop() { | ||
+ | //Nada | ||
+ | } | ||
+ | </syntaxhighlight> | ||
== Vea también == | == Vea también == | ||
Línea 20: | Línea 144: | ||
== Referencias == | == Referencias == | ||
* [https://www.arduino.cc/reference/en/language/structure/pointer-access-operators/dereference/ Guia de referencia] | * [https://www.arduino.cc/reference/en/language/structure/pointer-access-operators/dereference/ Guia de referencia] | ||
+ | * [https://www.luisllamas.es/buffer-circular-arduino/ Buffer circular en Arduino] - Luis LLamas | ||
+ | * [https://www.prometec.net/punteros/ Punteros] - Prometec | ||
+ | * [http://diwo.bq.com/arduino-variables-punteros/ Punteros] - Diwo | ||
+ | * [http://panamahitek.com/punteros-serial-arduino-parte-iii/ Punteros Arduino] - Panama Hitek | ||
[[Category:Estructura]] | [[Category:Estructura]] |
Revisión actual del 18:54 2 feb 2020
Contenido
Descripción
El operador de asterisco (*) tiene dos funciones:
- Declarar variables como punteros de cierto tipo de dato.
- Desreferenciar; dicho en otras palabras, trabajar con el valor apuntado en vez del puntero en sí. Dicho valor dependerá del tipo de dato del puntero (es "interpretado" segun la posición de memoria dada).
Nota: A menos que el microcontrolador tenga más de 32 KB de SRAM, los punteros siempre serán variables enteras de 16 bits (el equivalente a word) independientemente del tipo que interpreten. De hecho también se pueden realizar las típicas operaciones matemáticas en punteros como cualquier otro tipo número-entero de variable conocido.
Sintaxis
Declara un puntero. La diferencia con la declaracion de una variable normal radica en el * después del tipo de dato y antes del nombre.
tipo* puntero;
Asigne el valor apuntado por variable2. Cuando no es en una declaración, se usa para desreferenciar. El * va antes del nombre del puntero.
variable1 = *variable2;
Parámetros
- puntero
- Una variable que se utiliza como puntero, y que la posición apuntada será interpretada como si fuera un dato de tipo tipo.
- variable1
- Una variable normal (que solamente almacena un dato del tipo declarado).
- *variable2
- Puntero del cuál se quiere obtener el valor de la posición a la que apunta.
Comentarios
Los punteros son uno de los temas más complicados para los principiantes de Arduino y es posible escribir la gran mayoría de bocetos sin tener que encontrarse con los punteros. Sin embargo, para la manipulación de ciertas estructuras de datos, el uso de punteros puede simplificar el código, y el conocimiento de la manipulación de punteros es útil para tener en la propia caja de herramientas.
Bien usados, los punteros son excepcionalmente útiles para resolver cierto tipo de problemas, pero aprender a manejarlos puede provocar serios dolores de cabeza, especialmente cuando tratas de depurar un programa que se niega a funcionar como debe.
En primer lugar debemos entender que la memoria del Arduino está numerada en posiciones. Cada posición de memoria tiene una dirección única, que debe ser especificada cuando queremos leer o escribir su valor. Si miramos el tipo de memoria de los distintos Arduinos, vemos que, por ejemplo, el UNO dispone de 32 K de memoria flash para almacenamiento de programas y de 2 K de SRAM para almacenar de variables.
Cuando definimos una variable en el boceto, el compilador le asigna una posición en la memoria SRAM. Si la variables es del tipo char o byte asigna un byte de memoria, si es del tipo int o unsigned int le asigna dos posiciones de memoria y si es un tipo long o unsigned long le asigna 4 posiciones de memoria.
int numero; //Declaramos variable numero = 123; //Definimos variable
Nombre | Dirección de memoria | Contenido |
---|---|---|
numero | 2050 | 123 |
Esto nos da una idea de lo que puede suceder si escribimos un valor long en una dirección de memoria que corresponde a un int. Como el tipo long ocupa 4 bytes, cuando intentemos meterlos en una dirección a la que se ha asignado 2 bytes, va a ocupar el contenido de las siguientes posiciones de memoria, que pueden estar usadas por otros satos. Prueba esto:
void setup(){ Serial.begin(115200); int numero; long n = 100000; numero = n; //Estas intentando guardar 4 byte donde solo reservaste 2 bytes Serial.println(numero); //-31072 } void loop(){ //Nada }
Cuando declaras una matriz, en realidad estás declarando un puntero. De hecho es posible utilizar [] en cualquier puntero, aunque este no se haya declarado explícitamente como una matriz.
La diferencia entre declarar un puntero y una matriz, está en lo que ocurre implícitamente: declarar un puntero es igual que declarar cualquier otra variable simple; mientras que declarar una matriz es más que asignar 2 bytes, es asignar otro espacio según el tipo de dato y el número ingresado entre los [] (o en su defecto, la cantidad de elementos que aparece entre los {}), inicializarlo con ceros o con los valores preestablecidos, y finalmente asignarle al respectivo puntero la posición del primer byte de ese espacio que tuvo que asignar.
El acceso a un elemento es más amigable hacerlo con los [], sin embargo existen dos formas de realizar la misma acción:
matriz[i] = 4; *(matriz + i) = 4; // Ambos hacen exactamente lo mismo
De las dos formas es válido ya que una matriz es un puntero, y en cierto modo también se puede decir que al revés.
Advertencias
- Errores de overflow de registro no son advertidos por el compilador, ya que el puntero puede apuntar en cualquier posición arbitraria de la memoria. Tanto así que puede apuntarse a sí mismo, a otro puntero, e incluso a cualquier variable, matriz u objeto existente en el momento.
- Un gran poder conlleva una gran responsabilidad. Debido a que el puntero tiene el "superpoder" de meterse en cualquier parte de la memoria de ejecución (SRAM), existe el riesgo de corromperla sin previo aviso y colgar el programa en cuestión.
Ejemplo 1
byte *p; i=5, resultado;
p = &i; //5
resultado = *p; //resultado toma el valor de la posicion de memoria de p
Ejemplo 2
Implementamos un buffer circular con puntero.
const int ventana = 5;
int circularBuffer[ventana];
int* indice = circularBuffer;
void meter(int valor){
*indice = valor;
indice++;
if (indice >= circularBuffer + ventana){
indice = circularBuffer;
}
}
void setup(){
Serial.begin(115200);
}
void loop(){
if (Serial.available()){
int x = Serial.parseInt();
meter(x);
for (byte n=0; n<ventana; n++){
Serial.print(circularBuffer[n]);
Serial.print(",");
}
Serial.println();
}
}
Ejemplo 3
No se puede crear una matriz de matrices. Pero si se puede usar punteros a matrices, asi:
void setup(){
Serial.begin(9600);
byte cero[] = { 0xA6, 0xD4, 0xAC, 0xF8 };
byte uno[] = { 0xE9, 0x48, 0xCC, 0xE3 };
byte dos[] = { 0x46, 0x6F, 0xA5, 0xF8 };
byte tres[] = { 0xC2, 0x9D, 0xFC, 0x33 };
byte* matriz[4] = {cero, uno, dos, tres}; //Usando puntero
for (byte i=0; i<4; i++){
for (byte j=0; j<4; j++){
Serial.print(matriz[i][j], HEX);
Serial.print(", ");
}
Serial.println();
}
}
void loop() {
//Nada
}
Vea también
- ampersan - (&)
Referencias
- Guia de referencia
- Buffer circular en Arduino - Luis LLamas
- Punteros - Prometec
- Punteros - Diwo
- Punteros Arduino - Panama Hitek