Esta lección está dedicada a las operaciones con bits (operaciones con bits, matemática de bits), de las cuales aprenderá a operar con bits: celdas de memoria elementales de un microcontrolador. Ya veremos más aplicaciones de las operaciones de bits en la lección sobre registros de arduino, ahora consideraremos todo con el mayor detalle posible. Este tema es una de las lecciones más difíciles de entender de este curso, así que averigüemos por qué necesita poder trabajar con bits:
- Trabajo flexible y rápido directamente con los registros del microcontrolador (incluso para escribir bibliotecas)
- Almacenamiento de datos más eficiente (empaquetar múltiples valores en una variable y desempaquetarlos)
- Almacenamiento de símbolos y otra información para pantallas matriciales (empaquetado en un byte)
- La implementación se hace más rápida
- Trabajar con registros de turno y otras piezas de hardware similares
- Poder analizar el código de otra persona
Este tutorial se basa en el tutorial de operaciones de bits original de Arduino, puede leerlo aquí ; todo se describe allí con un poco más de detalle.
Sistema binario y almacenamiento de datos.
Al comienzo del ciclo de lecciones (en la lección sobre tipos de datos), ya discutimos el tema de los sistemas de cálculo y la importancia del sistema binario. Echemos un vistazo rápido a cómo funciona todo.
La celda de memoria mínima que podemos cambiar es un bit, solo toma dos valores: 0 y 1. La celda de memoria mínima a la que podemos acceder (que tiene una dirección en la memoria) es un byte, un byte consta de 8 bits, cada uno toma su propia celda (nota: en otras arquitecturas puede haber más o menos bits en un byte, en esta lección estamos hablando de un AVR y un byte de 8 bits) . Por lo tanto, un byte es un bloque elemental de memoria al que podemos acceder y leer / escribir datos, el tipo de datos más bajo en Arduino se llama -byte.
Al referirnos a un byte, podemos manipular los bits que lo componen, esto es para lo que se utilizan las operaciones de bits. Si «incluimos» todos los bits en un byte, obtenemos el número 0b11111111 en binario, o 255 en decimal. Aquí debe recordar la importancia del poder del dos (2): absolutamente todo está vinculado a él en las operaciones de bits. Echemos un vistazo a las primeras 8 potencias de dos (comenzando en 0):
Potencia de 2 | Decimal | Posición de los bits |
0 | 1 | 0b00000001 |
1 | 2 | 0b00000010 |
2 | 4 | 0b00000100 |
3 | 8 | 0b00001000 |
4 | 16 | 0b00010000 |
5 | 32 | 0b00100000 |
6 | 64 | 0b01000000 |
7 | 128 | 0b10000000 |
Así, una potencia de dos “indica” explícitamente el número de bit en un byte, contando de derecha a izquierda ( nota: en otras arquitecturas puede ser diferente ). Permítanme recordarles que no es absolutamente importante en qué sistema de cálculo trabaje: al microcontrolador no le importa y él ve unos y ceros en todo. Si “sumamos” el byte completo en representación decimal de los bits, obtenemos solo 255: 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255 . Es fácil adivinar que el número 0b11000000 es igual a 128 + 64, es decir, 192. Así se obtiene todo el rango de 0 a 255, que cabe en un byte. Si toma dos bytes, todo será igual, simplemente habrá 16 celdas, lo mismo para 4 bytes – 32 celdas con unos y ceros, cada una tiene su propio número según la potencia de dos.
Comencemos a manipular los bits con lo más simple: las funciones macro que vienen con el núcleo Arduino.
Macros de manipulación de bits de Arduino.
La «biblioteca» Arduino.h tiene varias macros útiles que le permiten activar y desactivar bits en un byte:
Macros de Arduino.h | Actuar |
bitRead(x, n) | x =byte desde el que leer. n =qué bit leer, comenzando en 0 a la derecha. |
bitSet(x, n) | x =byte desde el que establece. n =qué bit establecer, comenzando en 0 a la derecha |
bitClear(x, n) | x =byte donde va a borrar un bit. n =qué bit borrar, comenzando en 0 a la derecha |
bitWrite(x, n, b) | x =byte a escribir. n=que bit escribir, comenzando en 0. b=valor a escribir en el bit (0 o 1). |
bit ( bit) | Devuelve 2 a la potencia especificada en bit |
Otras macros integradas | |
_BV ( bit ) | Devuelve 2 a la potencia de bit |
bit_is_set ( value, bit ) | Comprueba si el bit dentro del Byte value está a 1. |
bit_is_clear ( value, bit ) | Comprueba si el bit dentro del Byte value está a 0. |
Ejemplo simple:
// aquí myByte == 0 byte myByte = 0; // aquí myByte se convierte en 128 o 0b10000000 bitSet ( myByte, 7 ) ; // aquí myByte se convierte en 192 o 0b11000000 bitWrite ( myByte, 6, 1 ) ;
También hay una macro _BV() que se encuentra en otros archivos del kernel y generalmente es una macro estándar para otras plataformas y compiladores, hace lo mismo que bit( b ) – devuelve 2 a la potencia del segundo.
También en el núcleo de Arduino hay dos macros más para verificar el estado de un bit en un byte, bit_is_set () y bit_is_clear (), son convenientes de usar para construcciones condicionales con Si…
En general, esto ya es suficiente para un trabajo completo con registros. Dado que son macros, funcionan lo más rápido posible y no son peores que las operaciones de bits elementales escritas a mano. A continuación analizaremos el contenido de estas macros y veremos cómo funcionan, pero por ahora, nos familiarizaremos con las operaciones lógicas elementales.
Operaciones bit a bit.
Pasando a cosas más complejas. De hecho, son lo más simples posible para un microcontrolador, tan simples que se ejecutan en un ciclo de reloj. A 16 MHz (la mayoría de las placas Arduino), una operación tarda 0,0625 microsegundos.
Operación And, Y
And ( Y ), también es «multiplicación lógica», la realiza el operador Y, y devuelve lo siguiente:
0 y 0 == 0 //Operacion And en bits. 0 y 1 == 0 1 y 0 == 0 1 y 1 == 1
La aplicación principal de la operación AND es hacer una máscara de bits. Permite «tomar» solo los bits especificados de un byte:
myByte = 0b11001100 ; myBits = myByte & 0b10000111 ; // myBits ahora es 0b10000100
Es decir, con la ayuda de <Y> tomamos del bytes 0b11001100 sólo los bits 10000111 , a saber – 0b 1 1001 100 , y conseguimos 0b10000100.
También puede utilizar el operador compuesto – & = –
myByte = 0b11001100 ; myByte & = 0b10000000 ; // toma la parte más significativa // myByte ahora es 10000000
Operación OR, |
OR ( OR ), también es «suma lógica», la realiza el operador | – y devuelve lo siguiente:
0 | 0 == 0 // Tabla de verdad de OR. 0 | 1 == 1 1 | 0 == 1 1 | 1 == 1
La aplicación principal de la operación OR es establecer un bit en un byte:
myByte = 0b11001100 ; myBits = myByte | 0b00000001 ; // establece el bit # 0 // myBits ahora es 0b11001101 myBits = myBits | bit ( 1 ) ; // establecer bit # 1 // myBits ahora es 0b11001111
También puede utilizar el operador compuesto | =
myByte = 0b11001100 ; myByte | = 16; // 16, active el bit nº # 4 // myByte ahora es 0b11011100
Ya ha entendido que puede indicar los bits necesarios de varias maneras: en forma binaria (0b00000001 – bit cero) , en forma decimal (16 – el cuarto bit) o usando macros bit() o _BV(). (bit (7) da 128 o 0b10000000, _BV (7) hace lo mismo).
Operación Not, ~
Operación de bit NO ( NOT ). Operador ~ invierte un bit:
~ 0 == 1 ~ 1 == 0
También puede invertir un byte:
myByte = 0b11001100 ; myByte = ~ myByte; // invertir // myByte ahora es 00110011
Operación XOR, ^
El operador realiza la operación OR exclusiva bit a bit ( XOR ) ^ – y hace lo siguiente:
0 ^ 0 == 0 0 ^ 1 == 1 1 ^ 0 == 1 1 ^ 1 == 0
Esta operación se usa generalmente para invertir el estado de un solo bit:
myByte = 0b11001100 ; myByte ^ = 0b10000000 ; // invertir el 7mo bit // myByte ahora es 01001100
Es decir, tomamos el bit 7 en el byte 0b 1 1001100 y lo cambiamos a 0, resultó 0b 0 1001100, el resto de los bits no se tocaron.
Corrimiento de bits, >>
Bit shift es un operador muy poderoso que le permite literalmente «mover» los bits en un byte a derecha e izquierda usando operadores >> y <<, y en consecuencia con su compuesto >> = y << = Si los bits van más allá de los límites del bloque (8 bits, 16 bits o 32 bits), se pierden.
myByte = 0b00011100 ; myByte = myByte << 3; // mueve 3 a la izquierda // myByte ahora es 0b11100000 myByte >> = 5; // myByte ahora es 0b00000111 myByte >> = 2; // myByte ahora es 0b00000001 // ¡Se pierden los otros bits!
Un desplazamiento de bits no hace más que multiplicar o dividir un byte por una potencia de 2. Sí, esta es una operación de división realizada en un ciclo de procesador. Volveremos a esto a continuación. Mire cómo funciona el operador de turno y compárelo con las macros bit() y _BV().
1 << 0 == 1 1 << 1 == 2 1 << 2 == 4 1 << 3 == 8 ... 1 << 8 == 256 1 << 9 == 512 1 << 10 == 1024
¡Sí, esto es elevar a una potencia de dos !
Encender y apagar bits.
Recordemos el ejemplo del párrafo sobre el bit OR, sobre el ajuste del bit deseado . Estas variantes de código hacen lo mismo:
myByte = 0b11000011 ; // establece el bit número 3 de diferentes maneras // esencialmente lo mismo myByte | = ( 1 << 3 ) ; myByte | = bit ( 3 ) ; myByte | = _BV ( 3 ) ; bitSet ( myByte, 3 ) ; // myByte es 0b11001011
¿Qué tal configurar varios bits a la vez?
myByte = 0b11000011 ; // establece los bits # 3 y 4 de diferentes maneras // esencialmente lo mismo myByte | = ( 1 << 3 ) | ( 1 << 4 ) ; myByte | = bit ( 3 ) | bit ( 4 ) ; myByte | = _BV ( 3 ) | _BV ( 4 ) ; // myByte es 0b11011011
¿O apagar bits? Es un poco diferente aquí, usamos & = y ~
myByte = 0b11000011 ; // apaga el bit # 1 de diferentes maneras // esencialmente lo mismo myByte & = ~ ( 1 << 1 ) ; myByte & = ~ _BV ( 1 ) ; bitClear ( myByte, 1 ) ; // myByte es 0b11000001
¿Desactivar varios bits a la vez? ¡De nada!
myByte = 0b11000011 ; // apaga los bits # 0 y 1 de diferentes maneras myByte & = ~ ( ( 1 << 0 ) | ( 1 << 1 ) ) ; myByte & = ~ ( _BV ( 0 ) | _BV ( 1 ) ) ; // myByte es 0b11000000
Son estas construcciones las que se encuentran en códigos y bibliotecas de alto nivel, así es como se realiza el trabajo con los registros del microcontrolador.
Veamos como son las macros de bits en Arduino:
#define bitRead (value, bit) (((valor) >> (bit)) & 0x01) #define bitSet (value, bit) ((value) | = (1UL << (bit))) #define bitClear (value, bit) ((value) & = ~ (1UL << (bit))) #define bitWrite (value, bit, bitvalue) (bitvalue? bitSet (value, bit): bitClear (value, bit)) #define bit (b) (1UL << (b))
Cálculos rápidos.
Como dije, las operaciones bit a bit son las más rápidas. Si se requiere la máxima velocidad de los cálculos, se pueden optimizar y ajustar a las «potencias de dos», pero a veces el compilador lo hace por sí mismo, para más detalles, vea la lección sobre optimización de código . Consideremos las operaciones básicas:
- División por 2 ^ n – rotación de bits a derecha Por ejemplo, valor / 8 Se puede escribir como valor >> 3. El compilador no optimiza la división por sí solo, lo que permite acelerar esta operación unas 15 veces con la optimización manual.
- Multiplicación por 2 ^ n – rotación de bits a izquierda. Por ejemplo, valor * 8 Se puede escribir como valor << 3. El compilador optimiza la multiplicación en sí, por lo que no tiene sentido la optimización manual. Pero puedes encontrarlo en fuentes de otras personas.
- Resto de la división por 2 ^ n – máscara de bits en n bits menos significativos. Por ejemplo, valor % 8 Se puede escribir como val & 0b111. El compilador optimiza estas operaciones por sí solo, por lo que no tiene sentido la optimización manual. Pero puedes encontrarlo en fuentes de otras personas.
Nota: las operaciones anteriores solo funcionan con tipos de datos enteros.
Guardar en memoria.
Al usar operaciones de bits, puede ahorrar algo de memoria empaquetando datos en bloques. Por ejemplo, una variable como boolean ocupa 8 bits en la memoria, aunque solo acepta 0 y 1. Puede empaquetar 8 variables lógicas en un byte, por ejemplo, así:
Macro para el empaquetamiento de bits en un byte
// almacenar banderas como 1 bit // macros #define B_TRUE (bp, bb) (bp) | = (bb) #define B_FALSE (bp, bb) (bp) & = ~ (bb) #define B_READ (bp, bb) bool ((bp) & (bb)) // ¡Así es como almacenamos nuestras banderas, los valores deben ser potencias de dos! #define B_FLAG_1 1 #define B_FLAG_2 2 #define B_LED_STATE 4 #define B_BUTTON_STATE 8 #define B_BUTTON_FLAG 16 #define B_SUCCESS 32 #define B_END_FLAG 64 #define B_START_FLAG 128 // este byte tendrá 8 bits byte boolPack1 = 0; void setup () { // el punto es este: con las funciones de macro establecemos / leemos el bit en el byte boolPack1 // escribe 1 a la bandera B_BUTTON_STATE B_TRUE ( boolPack1, B_BUTTON_STATE ) ; // escribe 0 en la bandera B_FLAG_1 B_FALSE ( boolPack1, B_FLAG_1 ) ; // leemos la bandera B_SUCCESS boolean successFlag = B_READ ( boolPack1, B_SUCCESS ) ; // o usar en la condición if ( B_READ ( boolPack1, B_SUCCESS )) { // ejecutar si se cumple la condición } } void loop () { }
Lo mismo con funciones Arduino
// ejemplo de empaquetado de banderas de bits en bytes // usando funciones de arduino byte myFlags = 0; // todas las banderas estan False // puedes definir los nombres // dígitos en orden 0-7 #define FLAG1 0 #define FLAG2 1 #define FLAG3 2 #define FLAG4 3 #define FLAG5 4 #define FLAG6 5 #define FLAG7 6 #define FLAG8 7 void setup () { // establece FLAG5 en verdadero bitSet ( myFlags, FLAG5 ) ; // establece FLAG1 en verdadero bitSet ( myFlags, FLAG1 ) ; // establece FLAG1 en falso bitClear ( myFlags, FLAG1 ) ; // leer FLAG5 bitRead ( myFlags, FLAG5 ) ; // condición con bandera 7 if ( bitRead ( myFlags, FLAG7 )) { // si FLAG7 == verdadero ejecuta codigo aqui... } } void loop() {}
Ejemplo muy funcional
// opción para empaquetar banderas en una matriz. ¡MEJOR Y MÁS CONVENIENTE QUE LOS EJEMPLOS ANTERIORES! #define NUM_FLAGS 30 // número de banderas byte flags[NUM_FLAGS / 8 + 1]; // matriz de banderas comprimidas // ============== MACROS PARA TRABAJAR CON UN PAQUETE DE BANDERAS ============== // setear la bandera (paquete, número) #define setFlag (flag, num) bitSet (flag [(num) >> 3], (num) & 0b111) // borrar bandera (paquete, número) #define clearFlag (flag, num) bitClear (flag [(num) >> 3], (num) & 0b111) // escribe la bandera (paquete, número, valor) #define writeFlag (flag, num, state) ((state)? setFlag (flag, num): clearFlag (flag, num)) // lee la bandera (paquete, número) #define readFlag (flag, num) bitRead (flag [(num) >> 3], (num) & 0b111) // bortrar todas las banderas (paquete) #define clearAllFlags (flag) memset (flag, 0, sizeof (flag)) // lesetearvantar todas las banderas (paquete) #define setAllFlags (flag) memset (flag, 255, sizeof (flag)) // ============== MACROS PARA TRABAJAR CON UN PAQUETE DE BANDERAS ============== void setup () { Serial.begin(9600); clearAllFlags(flags); writeFlag(flags, 0, 1); writeFlag(flags, 10, 1); writeFlag(flags, 12, 1); writeFlag(flags, 15, 1); writeFlag(flags, 15, 0); writeFlag(flags, 29, 1); // mostrar todo for (byte i = 0; i < NUM_FLAGS; i++) Serial.print(readFlag(flags, i)); } void loop() { }
Ejemplo interesante de compresión.
De la misma manera, puede empaquetar cualquier otro dato de otros tamaños para un almacenamiento o compresión conveniente. Como ejemplo, mi la biblioteca microLED, se utiliza el siguiente algoritmo: inicialmente necesita almacenar tres colores en la memoria para cada LED, cada color tiene una profundidad de 8 bits, es decir, se gastan un total de 3 bytes por LED, rojo, verde y azul (RRRRRRRR GGGGGGGG BBBBBBBB). Para ahorrar espacio y comodidad de almacenamiento, puede comprimir estos tres bytes en dos (tipo de datos int). Por ejemplo se quedaría así: RRRRRGGG GGGBBBBB. Hay tres variables en cada colo, rojo, verde y azul, r=red, g=green, b=blue:
int rgb = (( r & 0b11111000 ) << 8 ) | (( g & 0b11111100 ) << 3 ) | (( b & 0b11111000 ) >> 3 ) ;
Por lo tanto, hemos descartado los bits menos significativos (a la derecha) del rojo y el azul, esta es la compresión. Cuantos más bits se descarten, con menor precisión será posible «descomprimir» el número. Por ejemplo, el número 0b10101010 (170 en decimal) se comprimió en cinco bits, cuando se comprimió, obtuvimos 0b10101 000, es decir, perdió los tres bits menos significativos, y el decimal ya resulta ser 168. Para empaquetar, se usa un cambio de bit y una máscara, por lo que tomamos los primeros cinco bits de rojo, seis verdes y cinco azules, y los empujamos al lugar correcto en la variable de 16 bits resultante. Eso es todo, el color se comprime y se puede almacenar.
Para desempaquetar, se usa la operación inversa: seleccionamos los bits necesarios usando una máscara y los volvemos a cambiar a un byte:
byte r = ( datos & 0b1111100000000000 ) >> 8; byte g = ( datos & 0b0000011111100000 ) >> 3; byte b = ( datos & 0b0000000000011111 ) << 3;
Por lo tanto, puede comprimir, descomprimir y simplemente almacenar datos pequeños en tipos de datos estándar. Tomemos otro ejemplo: necesita almacenar varios números en el rango de 0 a 3 de la manera más compacta posible, es decir, en representación binaria esto es 0b00, 0b01, 0b10 y 0b11. Vemos que 4 de esos números se pueden agrupar en un byte (el máximo toma dos bits). Empujamos:
// números por ejemplo byte val_0 = 2; // 0b10 byte val_1 = 0; // 0b00 byte val_2 = 1; // 0b01 byte val_3 = 3; // 0b11 byte val_pack = (( val_0 & 0b11 ) << 6 ) | (( val_1 & 0b11 ) << 4 ) | (( val_2 & 0b11 ) << 2 ) | ( val_3 & 0b11 ) ; // tenemos 0b10000111
Como en el ejemplo con LED, solo tomamos los bits necesarios (en este caso, los dos inferiores, 0b11) y los movemos a la distancia deseada. Para descomprimir, lo hacemos en orden inverso:
byte unpack_1 = ( val_pack & 0b11000000 ) >> 6; byte unpack_2 = ( val_pack & 0b00110000 ) >> 4; byte unpack_3 = ( val_pack & 0b00001100 ) >> 2; byte unpack_4 = ( val_pack & 0b00000011 ) >> 0;
Y recuperamos nuestros bytes. Además, la máscara se puede reemplazar con una nota más conveniente deslizando 0b11 a la distancia deseada:
byte unpack_1 = ( val_pack & 0b11 << 6 ) >> 6; byte unpack_2 = ( val_pack & 0b11 << 4 ) >> 4; byte unpack_3 = ( val_pack & 0b11 << 2 ) >> 2; byte unpack_4 = ( val_pack & 0b11 << 0 ) >> 0;
Bueno, ahora, siguiendo el patrón, puedes crear una función o macro para leer el paquete por ti mismo:
#define UNPACK (x, y) (((x) & 0b11 << ((y) * 2)) >> ((y) * 2))
De serie. println ( UNPACK ( val_pack, 3 )) ; De serie. println ( UNPACK ( val_pack, 2 )) ; De serie. println ( UNPACK ( val_pack, 1 )) ; De serie. println ( UNPACK ( val_pack, 0 )) ;
Tips y trucos para el trabajo con bits.
Se pueden hacer muchas cosas interesantes en las operaciones de bits, y funcionarán muy rápidamente y ocuparán poco espacio. En este artículo se puede encontrar una gran lista de trucos y trucos, hay muchos de ellos y todos con ejemplos. Hay otra pequeña colección de los trucos más simples y útiles aquí (inglés). He reunido y traducido los que he creído más interesantes y se muestran aquí debajo:
Ajuste n º bits
x | (1<<n)
Está apagando el enésimo bit
x & ~(1<<n)
Inversión enésimo bit
x ^ (1<<n)
Redondear a la potencia de dos más cercana
unsigned int v; v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++;
Redondear a la baja
n >> 0 5.7812 >> 0
Obtener el máximo
int maxInt = ~(1 << 31); int maxInt = (1 << 31) - 1; int maxInt = (1 << -1) - 1; int maxInt = -1u >> 1;
Obtener el número entero mínimo
int minInt = 1 << 31; int minInt = 1 << -1;
Obtener el máximo
long maxLong = ((long)1 << 127) - 1;
Multiplicación por 2
n << 1; // n*2
División por 2
n >> 1; // n/2
Multiplicación por la n ésima potencia de dos
n << m;
Dividiendo por m ésima potencia de dos
n >> m;
Resto de la división
n & 0b1;
Verificación de igualdad
(a^b) == 0; // a == b !(a^b)
Verificación de paridad (multiplicidad 2)
(n & 1) == 1;
Intercambio de valores
//version 1 a ^= b; b ^= a; a ^= b; //version 2 a = a ^ b ^ (b = a)
Obtener un valor absoluto
//version 1 x < 0 ? -x : x; //version 2 (x ^ (x >> 31)) - (x >> 31);
Máximo de dos
b & ((a-b) >> 31) | a & (~(a-b) >> 31);
Al menos dos
a & ((a-b) >> 31) | b & (~(a-b) >> 31);
Comprobando el mismo signo
(x ^ y) >= 0;
Cambio de signo
i = ~i + 1; // or i = (i ^ -1) + 1; // i = -i
2 n
1 << n;
¿Es el número una potencia de 2?
n > 0 && (n & (n - 1)) == 0;
Resto de 2 n por m
m & ((1 << n) - 1);
Promedio
(x + y) >> 1; ((x ^ y) >> 1) + (x & y);
Coger el m ésimo bit de n (menos significativo)
(n >> (m-1)) & 1;
El n ésimo bit de n (del más significativo al menos significativo)
n & ~(1 << (m-1));
Comprobar
if (x & (1<<n)) { n-th bit is set } else { n-th bit is not set }
Resaltando el bit incluido más a la derecha
x & (-x)
Destacando el bit más a la derecha
~x & (x+1)
Destacando el bit habilitado correcto
x | (x+1)
Destacando el bit correcto
x & (x-1)
n + 1
-~n
n – 1
~-n
Obtener valor negativo
~n + 1; (n ^ -1) + 1;
si (x == a) x = b; si (x == b) x = a;
x = a ^ b ^ x;
Intercambiar bits adyacentes
((n & 10101010) >> 1) | ((n & 01010101) << 1)
Diferentes bits de números más a la derecha m & n
(n^m)&-(n^m)
Bit de números más a la derecha común m & n
~(n^m)&(n^m)+1
Prioridad de operaciones.
Para no multiplicar paréntesis, necesita conocer la prioridad de las operaciones. En C ++, es así:
- ::
- ++
- —
- ()
- []
- .
- –>
- ++
- —
- +
- –
- !
- ~
- (type)
- *
- &
- sizeof
- new, new[]
- delete, delete[]
- .*
- –>*
- *
- /
- %
- +
- –
- <<
- >>
- <
- <=
- >
- >=
- ==
- !=
- &
- ^
- |
- &&
- ||
- ?:
- =
- +=
- -=
- *=
- /=
- %=
- <<=
- >>=
- &=
- ^=
- |=
Hola amigo, realmente nivel «Dios», estoy aprendiendo C/C++ tanto para Arduino como los AVR, y la venía llevando bien hasta las «macros a nivel bit Arduino….», después, ya me perdí jajaja calculo que falta practica con ejemplos reales. Ojalá tengas un canal de YouTube para mostrar todo esto, que me parece de un excelente nivel.
Gracias Ariel, en este oficio siempre estamos aprendiendo.