diff --git a/book/17-bits-y-mascaras.md b/book/17-bits-y-mascaras.md new file mode 100644 index 0000000..66b5d12 --- /dev/null +++ b/book/17-bits-y-mascaras.md @@ -0,0 +1,1329 @@ +# Capítulo 17 — El mundo binario: bits, bytes y máscaras + +> Capítulo avanzado. Se apoya en variables y tipos (`02`), operadores (`03`), `if` y bucles (`04`), +> funciones (`05`), punteros (`07`), `struct` (`08`) y tipos de ancho exacto (`16`). +> +> Acá bajás un piso: dejás de mirar **números** y empezás a mirar los **bits** que los componen. Con +> eso vas a poder meter ocho banderas en un solo byte, extraer campos de un archivo binario, hacer +> multiplicaciones y divisiones gratis, y entender de una vez qué son `BYTE`, `WORD`, `DWORD` y +> `QWORD`. + +--- + +## Cómo leer este capítulo + +Este es el capítulo que convierte "sé que existen los bits" en "los uso todos los días". No es +teórico: cada idea viene con **código para mirar**, y cada bloque cierra con **práctica** para que lo +manejes con las manos. + +Está dividido en **seis partes**. Después de cada concepto importante hay uno o varios ejercicios +cortos. **No los saltees**: son la mitad del capítulo. + +| Parte | De qué habla | La idea en una frase | +|---|---|---| +| **1** | El sustrato | Un bit es un interruptor; un byte son ocho interruptores en fila. | +| **2** | Los operadores `&`, `\|`, `^`, `~` | Cuatro formas de combinar dos filas de interruptores. | +| **3** | Máscaras | Una plantilla que deja pasar solo los bits que te interesan. | +| **4** | `<<` y `>>` | Correr los interruptores y, de yapa, multiplicar y dividir. | +| **5** | `BYTE`, `WORD`, `DWORD`, `QWORD` | Cajas de 8, 16, 32 y 64 bits, y cómo leer campos dentro de ellas. | +| **6** | Conceptos extra | Contar bits, potencia de dos, rotar, endianness, `bitfields` y más. | + +Los **ejercicios** se numeran en orden: `17.1`, `17.2`, `17.3`, y así. Cada uno trae su **escenario de +prueba** (qué entra y qué tiene que salir, nunca el algoritmo) para que **compiles y compruebes**. + +> **Regla de oro del libro:** el capítulo explica y muestra **cómo funciona** cada cosa (con código +> ilustrativo), pero **los ejercicios no traen la solución**. El enunciado te da el problema; la +> respuesta la escribís vos. + +--- + +# PARTE 1 — El sustrato: bit, byte y bases + +Antes de operar con bits, hay que verlos. Esta parte es corta y es la base de todo lo demás. + +## 1.1 El bit: un interruptor + +Pensá en un **interruptor de luz**. Tiene dos posiciones: **apagado** o **encendido**. No hay matices. +Un **bit** es exactamente eso: un interruptor que puede valer **0** (apagado) o **1** (encendido). + +Con **un** interruptor solo podés representar dos cosas. Con **dos**, cuatro combinaciones +(`00`, `01`, `10`, `11`). Con **ocho**, doscientas cincuenta y seis (`2⁸`). Esa regla es la única que +tenés que recordar: + +```text +n bits → 2ⁿ combinaciones distintas +``` + +## 1.2 El byte: ocho interruptores en una caja + +Un **byte** es, en la práctica de hoy, **8 bits**. Los lenguajes modernos garantizan que +`sizeof(char) == 1`, pero **no** que un carácter tenga 8 bits: eso lo dice otra constante, +`CHAR_BIT`, que en todas las máquinas que vas a tocar vale **8**. + +```c +#include +#include /* define CHAR_BIT */ + +int main(void) { + /* sizeof(char) SIEMPRE es 1: un char ocupa "un byte". + Pero "un byte" tiene CHAR_BIT bits. En x86/x64/ARM, CHAR_BIT == 8. */ + printf("sizeof(char) = %zu\n", sizeof(char)); + printf("CHAR_BIT = %d\n", CHAR_BIT); /* casi siempre: 8 */ + return 0; +} +``` + +Los bits de un byte se numeran **de derecha a izquierda**, empezando en **0**. Es una convención, no un +capricho: el bit 0 es el **menos significativo** (el que vale `2⁰ = 1`) y el bit 7, el **más +significativo** (el que vale `2⁷ = 128`). + +```text + bit: 7 6 5 4 3 2 1 0 + ┌────┬────┬────┬────┬────┬────┬────┬────┐ + byte │ 0 │ 1 │ 0 │ 1 │ 1 │ 0 │ 0 │ 1 │ + └────┴────┴────┴────┴────┴────┴────┴────┘ + valor: 128 64 32 16 8 4 2 1 + (los que están en 1 se suman) + = 0+64+0+16+8+0+0+1 = 89 +``` + +## 1.3 Bases: por qué escribimos `0x` y `0b` + +Tu computadora piensa en **binario**, pero nadie escribe `0b01011001` a mano todo el tiempo: es +larguísimo y un dígito de más te arruina todo. Por eso usamos **atajos**: + +| Base | Prefijo | Ejemplo | Cuánto vale | +|---|---|---|---| +| Decimal | (ninguno) | `89` | 89 | +| Hexadecimal | `0x` | `0x59` | 89 | +| Octal | `0` | `0131` | 89 | +| Binario | `0b` | `0b01011001` | 89 | + +El **hexadecimal** es el atajo estrella porque **cada dígito hex son exactamente 4 bits** (un +*nibble*, del inglés *nibble*, "mordisco"). Entonces un byte entra justo en **dos dígitos hex**. +Pasás de binario a hex **de a cuatro bits**, sin cuentas: + +```text + 0101 1001 ← el byte partido en dos nibbles + 5 9 ← cada nibble, su dígito hex + = 0x59 +``` + +> **Momento ajá.** Los dígitos hex van del `0` al `9` y después de la `A` a la `F` (`A=10`, `B=11`, +> … `F=15`). ¿Por qué letras? Porque con un solo símbolo hay que representar **16** valores y solo +> tenemos **10** dígitos. Así que se usan seis letras. Nada más. + +> **El prefijo binario `0b` es de C23.** Se estandarizó recién ahí (y con él, el separador `'` para +> agrupar dígitos: `0b0101'1001`). En compiladores viejos, `0b` era una **extensión** de GCC/Clang +> que quizá no estaba activada. Si te sale un error raro con `0b`, revisá el estándar: compilá con +> `-std=c23` (o usá `0x`). + +## 1.4 El byte también guarda negativos: complemento a dos + +Un byte sin signo (`unsigned char`) va de `0` a `255`. Pero un byte **con signo** (`signed char`, +y `char` en la mayoría de las plataformas) va de **`-128` a `127`**. ¿Cómo entra un número negativo en +una caja de bits que no tiene signo? + +C dijo "el bit **más alto** decide": si el bit 7 vale **1**, el número es **negativo**, y su valor se +lee con la regla del **complemento a dos**: para saber cuánto vale, **invertís todos los bits y sumás +1**. + +```text + 0b11111111 → invertir: 00000000 → +1 → -1 + 0b11111110 → invertir: 00000001 → +1 → -2 + 0b10000000 → invertir: 01111111 → +1 → -128 (el más negativo) +``` + +Esa es la razón de que el rango sea asimétrico: el `0` ocupa una sola combinación (`00000000`), así +que del lado negativo sobra un lugar: hay un `-128` pero no un `+128`. + +> **Ojo con esto.** El **mismo byte** `0xFF` es **255** si lo tratás como `unsigned char` y **-1** si +> lo tratás como `signed char`. Los bits no cambian: cambia **cómo los interpretás**. Por eso importa +> tanto el **tipo** (capítulo `16`): es el que le dice a C cómo leer esos bits. + +## 1.5 Extensión de signo: el detalle que muerde + +Cuando un número "chico" se convierte en uno "grande", C tiene que rellenar los bits nuevos. Y ahí se +comporta distinto según el signo: + +- **Sin signo**: rellena con **0**. `(unsigned)0x9` → `0x00000009`. +- **Con signo**: **copia el bit de signo** (extensión de signo). `(int)-1` → `0xFFFFFFFF`. + +```c +#include + +int main(void) { + signed char sc = -1; /* 0xFF en 8 bits */ + unsigned char uc = 255; /* 0xFF en 8 bits */ + + /* %x imprime en hexadecimal (sin signo). Mirá los ceros de relleno: + sc (con signo) propaga el 1 del signo; uc (sin signo) rellena con 0. */ + printf("sc = %08x\n", (unsigned)sc); /* ffffffff */ + printf("uc = %08x\n", (unsigned)uc); /* 000000ff */ + + return 0; +} +``` + +> **Para guardar.** Antes de operar con bits, preguntate: **¿este valor es un número o un montón de +> bits?** Si es un montón de bits (una máscara, un byte de imagen), usá **tipos sin signo** +> (`uint8_t`, `uint32_t`). Evitás sorpresas como las de arriba. La regla corta: **bits se manipulan +> en `unsigned`.** + +## Práctica de la Parte 1 + +### Ejercicio 17.1 — Radiografía de un byte + +**Conceptos** · bits, valor posicional, `uint8_t`, `printf` (`%x`, `%b` o `%u`) + +**Contexto** · Para entender los bits hay que verlos. Simplificación: mostrás un byte en decimal, +hexadecimal y binario, y desglosás qué bits están encendidos. + +**Consigna** · Pedí (o fijá) un valor de `0` a `255` en un `uint8_t`. Imprimí: + +1. el valor en **decimal**; +2. el mismo valor en **hexadecimal** (`%x`), con dos dígitos; +3. el mismo valor en **binario**. Podés usar el `%b` de C23 o, si compilás con un estándar anterior, + construir el texto bit a bit con un bucle; +4. la lista de **posiciones** de los bits que están en `1` (de mayor a menor o al revés, a tu gusto). + +**Entrada** · Un valor de `0` a `255`. +**Salida** · El valor en las tres bases y la lista de bits encendidos. + +**Escenario de prueba** + +```text +valor = 89 +decimal : 89 +hexadecimal : 0x59 +binario : 01011001 +bits en 1 : 6, 4, 3, 0 (64 + 16 + 8 + 1 = 89) +``` + +```text +valor = 255 +decimal : 255 +hexadecimal : 0xff +binario : 11111111 +bits en 1 : 7, 6, 5, 4, 3, 2, 1, 0 +``` + +**Identificadores sugeridos** · `value`, `mask`, `bitIndex`, `printBinary` +**Criterios de aceptación** +- [ ] El valor se guarda en un `uint8_t` (sin signo). +- [ ] Las tres bases coinciden para los dos escenarios. +- [ ] La lista de bits encendidos suma exactamente el valor original. +- [ ] El binario se muestra con **ocho** dígitos (con ceros a la izquierda). + +--- + +### Ejercicio 17.2 — ¿Cuánto vale ese bit de signo? + +**Conceptos** · complemento a dos, `int8_t` vs `uint8_t`, `0xFF`, interpretación según el tipo + +**Contexto** · El mismo byte significa dos cosas distintas según lo leas con o sin signo, y eso +confunde a cualquiera la primera vez. Simplificación: mostrás las dos interpretaciones de los mismos +bits. + +**Consigna** · Tomá el byte `0xFF` y guardalo en un `uint8_t` y en un `int8_t`. Imprimí **ambos** +valores y el **mismo** byte en hex. Después explicá en un comentario **por qué** el mismo patrón de +bits da dos números distintos. + +**Entrada** · Ninguna (el byte es fijo, `0xFF`). +**Salida** · Los dos valores y el hex. + +**Escenario de prueba** + +```text +bits : 11111111 (0xff) +uint8_t : 255 +int8_t : -1 +explicación: el tipo decide si el bit 7 es "128" (sin signo) o el signo (complemento a dos, con signo). +``` + +**Identificadores sugeridos** · `rawByte`, `asUnsigned`, `asSigned` +**Criterios de aceptación** +- [ ] El mismo patrón de bits se imprime igual en hex. +- [ ] Sin signo da `255`; con signo da `-1`. +- [ ] La explicación menciona el bit de signo y el complemento a dos. +- [ ] No se cambian los bits: solo la **interpretación**. + +--- + +# PARTE 2 — Los operadores de bits: `&`, `|`, `^` y `~` + +Acá está el corazón del capítulo. Son **cuatro** operadores (más dos de desplazamiento, que vienen en +la Parte 4), y se aplican **bit por bit**, columna por columna. Se parecen a los lógicos, pero **no** +son los mismos: `&` no es `&&`, y `|` no es `||`. + +> **No los confundas.** +> - `&` (un solo ampersand) = AND **de bits**, opera sobre cada bit. +> - `&&` (dos) = AND **lógico**, devuelve `0` o `1` según si ambos son "verdaderos". +> - `|` (una barra) = OR **de bits**. `||` (dos) = OR **lógico**. +> La diferencia es la misma que entre *"multiplicá cada dígito"* y *"¿es el número distinto de cero?"*. + +## 2.1 `&` — AND: "solo si los dos están encendidos" + +El AND bit a bit compara **dos bits** y devuelve `1` **solo si ambos son `1`**. Si alguno es `0`, el +resultado es `0`. Pensalo como **dos amigos que tienen que estar de acuerdo**: si uno dice que no, no +hay trato. + +| `a` | `b` | `a & b` | +|---|---|---| +| 0 | 0 | 0 | +| 0 | 1 | 0 | +| 1 | 0 | 0 | +| 1 | 1 | **1** | + +```c +#include + +int main(void) { + uint8_t a = 0b11001100; /* 0xCC */ + uint8_t b = 0b10101010; /* 0xAA */ + + /* & compara cada bit con su vecino de la otra fila. + Solo queda en 1 donde AMBAS tienen 1. */ + uint8_t r = a & b; /* 0b10001000 = 0x88 */ + + printf("a = %08b (0x%02x)\n", a, a); /* 11001100 (0xcc) */ + printf("b = %08b (0x%02x)\n", b, b); /* 10101010 (0xaa) */ + printf("a & b = %08b (0x%02x)\n", r, r); /* 10001000 (0x88) */ + return 0; +} +``` + +## 2.2 `|` — OR: "si alguno está encendido" + +El OR bit a bit devuelve `1` si **al menos uno** de los dos bits es `1`. Solo da `0` cuando **los +dos** son `0`. Es el operador de **encender**: sirve para "prender" bits concretos. + +| `a` | `b` | `a \| b` | +|---|---|---| +| 0 | 0 | 0 | +| 0 | 1 | **1** | +| 1 | 0 | **1** | +| 1 | 1 | **1** | + +```c +uint8_t a = 0b11001100; +uint8_t b = 0b10101010; +uint8_t r = a | b; /* 0b11101110 = 0xEE */ +``` + +## 2.3 `^` — XOR: "encendido solo si son DISTINTOS" + +El XOR (OR exclusivo) devuelve `1` **solo si los dos bits son distintos**. Si son iguales, da `0`. Es +un **detector de diferencias** y, además, el único operador que **invierte** un bit cuando le aplicás +un `1` (por eso es el operador de *toggle*, ver Parte 3). + +| `a` | `b` | `a ^ b` | +|---|---|---| +| 0 | 0 | 0 | +| 0 | 1 | **1** | +| 1 | 0 | **1** | +| 1 | 1 | 0 | + +```c +uint8_t a = 0b11001100; +uint8_t b = 0b10101010; +uint8_t r = a ^ b; /* 0b01100110 = 0x66 */ +``` + +> **Dato de color.** El XOR tiene una propiedad mágica que lo hace famoso: **aplicarlo dos veces +> devuelve el original** (`x ^ k ^ k == x`). Por eso se usa en cifrados simples de juguete y en el +> truco de "intercambiar dos variables sin variable temporal". Es lindo, pero **`^` con la misma +> variable dos veces da 0**: no te olvides de eso. + +## 2.4 `~` — NOT: "invertí todo" + +El complemento (`~`) es el único que trabaja con **un** operando: agarra cada bit y lo **da vuelta** +(`0`→`1`, `1`→`0`). Es el operador de la **negación**, y es la pieza clave para **apagar** bits (Parte +3). + +| `a` | `~a` | +|---|---| +| 0 | 1 | +| 1 | 0 | + +```c +#include + +int main(void) { + uint8_t a = 0b11001100; + /* ~ invierte cada bit. Ojo: se evalúa promoviendo a int, + por eso conviene asignar a un uint8_t para "recortar" a 8 bits. */ + uint8_t r = (uint8_t)~a; /* 0b00110011 = 0x33 */ + + printf("a = %08b (0x%02x)\n", a, a); /* 11001100 (0xcc) */ + printf("~a = %08b (0x%02x)\n", r, r); /* 00110011 (0x33) */ + return 0; +} +``` + +> **La identidad que no se olvida.** Con números enteros, `~x == -x - 1`. Por eso `~0` es `-1` +> (`0xFFFFFFFF`) y `~5` es `-6`. No es una regla aparte: es el complemento a dos otra vez. + +## 2.5 La tabla de un solo vistazo + +| Operador | Nombre | Hace… | Regla corta | Uso típico | +|---|---|---|---|---| +| `&` | AND | deja pasar si ambos son 1 | **filtrar / testear** | "¿está encendido el bit?" | +| `\|` | OR | enciende si alguno es 1 | **prender** | "encendé estos bits" | +| `^` | XOR | enciende si son distintos | **invertir / comparar** | "cambiá este bit" | +| `~` | NOT | da vuelta todo | **negar** | "apagá este bit" (`& ~`) | +| `<<` | shift izq | corre a la izquierda | **×2ᵏ** | "movete k lugares" | +| `>>` | shift der | corre a la derecha | **÷2ᵏ** | "movete k lugares" | + +## Práctica de la Parte 2 + +### Ejercicio 17.3 — La calculadora de bits + +**Conceptos** · `&`, `|`, `^`, operaciones bit a bit, entrada de dos bytes + +**Contexto** · Antes de usar máscaras hay que ver los operadores "en crudo". Simplificación: una +calculadora que combina dos bytes con cada operador. + +**Consigna** · Leé **dos** valores de `0` a `255` (por ejemplo con `strtoul` en base 0, para aceptar +`0x` y `0b`, o con `scanf`). Para ese par, mostrá el resultado de `&`, `|`, `^` y `~` **de cada uno**, +en decimal, hex y binario. Mostrá también **la tabla de verdad** (los cuatro casos) para cada operador, +como comprobación. + +**Entrada** · Dos bytes (adminsitrá base `0x`, `0b` y decimal). +**Salida** · Una fila por operación con los tres formatos. + +**Escenario de prueba** + +```text +a = 0xCC b = 0xAA +a & b = 0x88 (10001000) +a | b = 0xEE (11101110) +a ^ b = 0x66 (01100110) +~a = 0x33 (00110011) +~b = 0x55 (01010101) +``` + +**Identificadores sugeridos** · `left`, `right`, `andResult`, `orResult`, `xorResult` +**Criterios de aceptación** +- [ ] Se manejan valores **sin signo**. +- [ ] Cada resultado se imprime en decimal, hex y binario. +- [ ] La tabla de verdad de cada operador se muestra y coincide con la teoría. +- [ ] Los resultados del escenario salen exactamente como se espera. + +--- + +### Ejercicio 17.4 — El mapa de diferencias + +**Conceptos** · `^` como detector de diferencias, contar bits distintos + +**Contexto** · Comparar dos versiones de un mismo byte (por ejemplo, dos fotos de un estado) y saber +**qué bits cambiaron** es un caso real de XOR. Simplificación: contás y ubicás las diferencias entre +dos bytes. + +**Consigna** · Leé dos bytes. Usando `^`, mostrá: + +1. una **máscara** con `1` en cada posición donde los dos bytes **difieren**; +2. **cuántos** bits difieren (la cantidad de `1` de esa máscara); +3. la lista de las **posiciones** que difieren. + +**Entrada** · Dos bytes. +**Salida** · La máscara, el conteo y las posiciones. + +**Escenario de prueba** + +```text +a = 0b11001100 +b = 0b11000011 +a ^ b = 0b00001111 (0x0f) +bits distintos : 4 +posiciones : 0, 1, 2, 3 +``` + +**Identificadores sugeridos** · `a`, `b`, `diffMask`, `diffCount` +**Criterios de aceptación** +- [ ] La máscara de diferencias sale de **un** `^` entre los dos bytes. +- [ ] El conteo coincide con el número de posiciones listadas. +- [ ] Dos bytes iguales dan máscara `0` y conteo `0`. +- [ ] Se usa un tipo sin signo para los bytes. + +--- + +# PARTE 3 — Máscaras: un byte, ocho banderas + +Esta es **la** parte que pediste, y es la aplicación más común de todo lo anterior. Una **máscara** es +un valor con **un `1` en el/los bits que te interesan** y `0` en el resto. Es como un **calco** +(*stencil*, la plantilla con agujeros): la ponés encima y solo pasa la luz por los agujeros. + +## 3.1 La idea: ocho fuentes de luz, un solo interruptor general + +Imaginate una sala con **ocho lámparas** y **un tablero con ocho interruptores**. No querés ocho +cajas separadas: querés **una** caja de 8 bits donde cada bit sea el estado de una lámpara. Eso es +exactamente un byte usado como **conjunto de banderas** (*flags*). + +```text + bit: 7 6 5 4 3 2 1 0 + ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐ + flags │música│ wifi │avión │ linterna│ dato │ dato │ dato │ dato │ + └──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┘ + 128 64 32 16 8 4 2 1 +``` + +Con **un byte**, ocho banderas booleanas. Con un `uint32_t` (**DWORD**), **32**. Con un `uint64_t` +(**QWORD**), **64**. La técnica es siempre la misma; cambia el ancho de la caja. + +## 3.2 El truco del `1 << n`: fabricar una máscara + +Para una bandera en la posición `n`, la máscara es un `1` corrido `n` lugares a la izquierda. Eso se +escribe `1u << n` (el `u` la hace `unsigned`, para no meter signo en la fiesta). + +```text +n = 0 → 1u << 0 = 0b00000001 (0x01) +n = 3 → 1u << 3 = 0b00001000 (0x08) +n = 7 → 1u << 7 = 0b10000000 (0x80) +``` + +Y como cada bandera tiene su nombre, lo natural es **definir máscaras con nombre** en vez de repetir +el `1u << n` por todo el código: + +```c +#include + +/* Cada bandera es un bit propio. Los nombres dicen QUÉ son, no cuánto valen. */ +enum DeviceFlags { + FLAG_ON = 1u << 0, /* 0x01 bit 0 */ + FLAG_CONNECTED = 1u << 1, /* 0x02 bit 1 */ + FLAG_ERROR = 1u << 2, /* 0x04 bit 2 */ + FLAG_UPDATE_READY = 1u << 3, /* 0x08 bit 3 */ + FLAG_LOW_BATTERY = 1u << 4, /* 0x10 bit 4 */ + FLAG_MUTED = 1u << 5, /* 0x20 bit 5 */ + FLAG_CHARGING = 1u << 6, /* 0x40 bit 6 */ + FLAG_SAFE_MODE = 1u << 7 /* 0x80 bit 7 */ +}; +``` + +> **Por qué esto es mejor que ocho `bool`.** Ocho `bool` ocupan **8 bytes** (cada uno, aunque valga +> un bit, ocupa un byte entero). Las ocho banderas en **un `uint8_t` ocupan 1 byte**. Cuando eso se +> repite en un millón de registros, la diferencia deja de ser un detalle. Es el mismo motivo del +> capítulo `16` (sección 16.9): tipos chicos ahorran **cuando hay muchos**. + +## 3.3 Las cuatro operaciones sobre una bandera + +Con las cuatro herramientas de la Parte 2, hacés todo lo que necesitás con una bandera: **testear**, +**prender**, **apagar** e **invertir**. Esta es la tabla que vas a usar el resto de tu vida. + +| Quiero… | Escribo… | Operador | Por qué | +|---|---|---|---| +| **Testear** si está encendida | `flags & MASK` | `&` | solo queda `MASK` si el bit estaba en 1 | +| **Prender** (poner en 1) | `flags \| MASK` | `\|` | fuerza ese bit a 1, deja el resto igual | +| **Apagar** (poner en 0) | `flags & ~MASK` | `& ~` | el `~MASK` es "todos menos ese bit" | +| **Invertir** (toggle) | `flags ^ MASK` | `^` | cambia 1↔0 solo en ese bit | + +Mirá cómo queda cada una en código: + +```c +#include +#include + +enum { FLAG_ERROR = 1u << 2 }; /* 0x04 */ + +int main(void) { + uint8_t flags = 0; + + /* PRENDER: OR con la máscara fuerza el bit 2 a 1. */ + flags |= FLAG_ERROR; /* flags = 0b00000100 */ + printf("prendida : %08b\n", flags); + + /* TESTEAR: AND con la máscara. Si el resultado NO es 0, el bit estaba encendido. */ + if (flags & FLAG_ERROR) { /* el & da 0x04, distinto de 0 → verdadero */ + printf("la bandera esta encendida\n"); + } + + /* INVERTIR: XOR con la máscara da vuelta solo ese bit. */ + flags ^= FLAG_ERROR; /* flags = 0b00000000 */ + printf("invertida: %08b\n", flags); + + /* VOLVER A PRENDER y luego APAGAR: AND con el complemento del bit. */ + flags |= FLAG_ERROR; /* 0b00000100 */ + flags &= ~FLAG_ERROR; /* ~0x04 = 0xFB → apaga el bit 2 */ + printf("apagada : %08b\n", flags); + + return 0; +} +``` + +> **La trampa N.º 1: testear con `==`.** No escribas `if (flags == FLAG_ERROR)`: eso pregunta si +> **todas** las banderas valen exactamente eso, no si **una** está encendida. Se testea con `&`, y la +> condición es "el resultado es distinto de cero". La forma idiomática es +> `if (flags & FLAG_ERROR)`, no `if ((flags & FLAG_ERROR) == FLAG_ERROR)` (aunque también funciona). + +> **La trampa N.º 2: el `~` y el ancho.** `~FLAG_ERROR` sobre un `int` de 32 bits da +> `0xFFFFFFFB`, no `0xFB`. Al hacer `flags &= ~FLAG_ERROR` con `flags` de 8 bits, los bits de arriba +> del `~` se recortan al asignar, así que **funciona igual**. Pero si operás con tipos de distinto +> ancho en un mismo cálculo, tenelo presente. + +## 3.4 Varias banderas de una: preguntar por un grupo + +Un `&` te deja preguntar por **varias** banderas juntas. Combinando máscaras con `|` armás "grupos": + +```c +/* ¿Están las DOS banderas encendidas a la vez? (AND con la suma de máscaras == la suma) */ +if ((flags & (FLAG_ERROR | FLAG_LOW_BATTERY)) == (FLAG_ERROR | FLAG_LOW_BATTERY)) { + /* ambas encendidas */ +} + +/* ¿Está encendida ALGUNA de las dos? (el AND no es 0) */ +if (flags & (FLAG_ERROR | FLAG_LOW_BATTERY)) { + /* al menos una encendida */ +} +``` + +> **Para guardar.** `flags & GRUPO` = "¿alguna de estas?" (intersección no vacía). +> `(flags & GRUPO) == GRUPO` = "¿todas estas?" (todas presentes). Son dos preguntas **distintas**, y +> confundirlas es de los bugs más comunes. + +## Práctica de la Parte 3 + +### Ejercicio 17.5 — El panel de control + +**Conceptos** · máscaras, `1u << n`, `&`, `|`, `~`, `^`, banderas + +**Contexto** · Todo panel de control (un router, un reproductor, una consola de juegos) mantiene su +estado en un conjunto de banderas. Simplificación: un panel de 8 banderas en un `uint8_t`. + +**Consigna** · Definí **ocho** banderas con nombre, una por bit, para un panel de control (por +ejemplo: `POWER`, `RECORDING`, `MUTED`, `NETWORK`, `ERROR`, `PAUSED`, `OVERHEATED`, `BATTERY_LOW`). +Implementá **cinco** funciones que reciban el estado (`uint8_t`) y: + +1. **prender** una bandera dada; +2. **apagar** una bandera dada; +3. **invertir** una bandera dada; +4. **testear** una bandera dada (devolver un `bool`); +5. **contar** cuántas banderas están encendidas. + +Demostralas encendiendo unas cuantas, invirtiendo una, apagando otra y mostrando el estado en binario +después de cada paso. + +**Entrada** · Una secuencia de operaciones (o fija, en el `main`). +**Salida** · El estado del panel en binario después de cada operación, y el conteo final. + +**Escenario de prueba** + +```text +inicio : 00000000 (0 encendidas) +prender RECORDING : 00000100 (1 encendida) +prender NETWORK : 00010100 (2 encendidas) +invertir MUTED : 00110100 (3 encendidas) +apagar RECORDING : 00110000 (2 encendidas) +total encendidas : 2 +``` + +**Identificadores sugeridos** · `PanelState`, `setFlag`, `clearFlag`, `toggleFlag`, `isFlagSet`, +`countFlags` +**Criterios de aceptación** +- [ ] Las ocho banderas se definen con `1u << n` y con nombres significativos. +- [ ] Prender usa `|`; apagar usa `& ~`; invertir usa `^`; testear usa `&`. +- [ ] El conteo recorre los bits (no cuenta a mano). +- [ ] El escenario sale igual, y dos banderas nunca comparten el mismo bit. + +--- + +### Ejercicio 17.6 — Permisos al estilo Unix + +**Conceptos** · máscaras, grupos de banderas, `&` con grupo, `==` con grupo + +**Contexto** · Los permisos de archivo de Unix (`rwx` para dueño, grupo y otros) se guardan en bits +justamente para operarlos con máscaras. Simplificación: modelás **solo** los permisos de **un** grupo +sobre un `uint8_t` (aunque los reales son 9 bits en total). + +**Consigna** · Definí tres banderas: `READ` (leer), `WRITE` (escribir), `EXEC` (ejecutar). Implementá +funciones que digan: + +1. si se puede **leer** (`READ`); +2. si se puede **escribir** (`WRITE`); +3. si se puede **ejecutar** (`EXEC`); +4. si **todos** los permisos están dados (testear con grupo y `==`); +5. si **alguno** de leer/escribir está dado (testear con grupo). + +Mostrá el resultado para varios conjuntos de permisos. + +**Entrada** · Varios valores de permisos (por ejemplo `0b111`, `0b100`, `0b010`, `0b000`). +**Salida** · El veredicto de cada función para cada conjunto. + +**Escenario de prueba** + +```text +permisos = rwx (111): leer=sí escribir=sí ejecutar=sí todos=sí leer-o-escribir=sí +permisos = r-- (100): leer=sí escribir=no ejecutar=no todos=no leer-o-escribir=sí +permisos = -w- (010): leer=no escribir=sí ejecutar=no todos=no leer-o-escribir=sí +permisos = --- (000): leer=no escribir=no ejecutar=no todos=no leer-o-escribir=no +``` + +**Identificadores sugeridos** · `Permissions`, `canRead`, `canWrite`, `canExec`, `canAll`, +`canReadOrWrite` +**Criterios de aceptación** +- [ ] "Todos" usa `(p & GRUPO) == GRUPO`; "alguno" usa `(p & GRUPO) != 0`. +- [ ] Cada permiso es un bit distinto. +- [ ] Los cuatro escenarios dan exactamente lo esperado. +- [ ] El código distingue claramente "todos" de "alguno". + +--- + +### Ejercicio 17.7 — El registro de opciones de arranque + +**Conceptos** · banderas de 32 bits (`uint32_t`), máscaras altas, `1u << n` con `n` grande + +**Contexto** · Los programas serios reciben muchas opciones (verbose, debug, dry-run, sin color, …) y +las guardan como banderas. Con 8 bits te quedás corto enseguida, así que se usa un `uint32_t`. +Simplificación: **treinta y dos** opciones, aunque solo uses algunas. + +**Consigna** · Definí al menos **diez** banderas en un `uint32_t`, incluyendo algunas en **bits +altos** (por ejemplo el bit 31). Implementá una función que reciba el registro y **liste por nombre** +cada opción que esté encendida. Comprobá que una bandera en el bit 31 **no** se pierde ni se confunde +con las de abajo. + +**Entrada** · Un registro con varias opciones (incluida una del bit alto). +**Salida** · La lista de opciones activas, por nombre. + +**Escenario de prueba** + +```text +registro = 0x80000005 (bit 31 + bit 2 + bit 0) +opciones activas: + - VERBOSE (bit 0) + - DRY_RUN (bit 2) + - EXPERIMENTAL (bit 31) +``` + +**Identificadores sugeridos** · `StartupOptions`, `optionMask`, `listActiveOptions` +**Criterios de aceptación** +- [ ] El registro es `uint32_t` (sin signo, para que el bit 31 no sea "signo"). +- [ ] La bandera del bit 31 se define como `1u << 31` (o `0x80000000u`). +- [ ] El bit 31 se detecta correctamente (no aparece como negativo). +- [ ] Las banderas altas y bajas conviven sin pisarse. + +--- + +# PARTE 4 — Desplazamientos: `<<` y `>>` + +Los otros dos operadores. Mueven **toda la fila de bits** a la izquierda o a la derecha. Es como +correr un tren: los vagones que salen por un extremo se pierden, y por el otro extremo entran vagones +nuevos (que son **ceros**). + +## 4.1 `<<` y `>>`: correr los bits + +- `x << k`: cada bit se corre **k lugares a la izquierda**. Los bits que se salen por la izquierda se + **pierden**, y por la derecha entran **ceros**. +- `x >> k`: cada bit se corre **k lugares a la derecha**. Los bits que se salen por la derecha se + **pierden**, y por la izquierda entran... depende del signo (ver 4.4). + +```text + 0b00001111 << 2 = 0b00111100 (se corrió a la izquierda; entran dos ceros) + └ izquierda derecha ┘ + + 0b11000000 >> 3 = 0b00011000 (se corrió a la derecha; entran tres ceros) +``` + +```c +#include + +int main(void) { + uint8_t x = 0b00001111; /* 0x0F */ + + printf("%08b << 1 = %08b\n", x, (uint8_t)(x << 1)); /* 00011110 */ + printf("%08b << 4 = %08b\n", x, (uint8_t)(x << 4)); /* 11110000 */ + printf("%08b >> 2 = %08b\n", x, (uint8_t)(x >> 2)); /* 00000011 */ + return 0; +} +``` + +> **El `<<` también es la fábrica de máscaras.** Todas las máscaras de la Parte 3 (`1u << n`) son un +> desplazamiento. Por eso el `<<` aparece en todos lados: es tanto "mover" como "fabricar". + +## 4.2 Multiplicar por 2, 4, 8… con `<<` + +Correr **un** bit a la izquierda **duplica el valor**. Correr **k** lugares lo multiplica por `2ᵏ`. +No es magia: es que cada bit vale el **doble** que el de su derecha. + +```text + 0b00000101 = 5 + 5 << 1 = 0b00001010 = 10 (×2) + 5 << 2 = 0b00010100 = 20 (×4) + 5 << 3 = 0b00101000 = 40 (×8) +``` + +## 4.3 Dividir (hacia abajo) con `>>` + +Igual pero al revés: `x >> k` **divide** entre `2ᵏ`, **truncando** hacia el entero de abajo (para +valores sin signo). + +```text + 0b00101000 = 40 + 40 >> 1 = 0b00010100 = 20 (÷2) + 40 >> 2 = 0b00001010 = 10 (÷4) + 40 >> 3 = 0b00000101 = 5 (÷8) +``` + +## 4.4 Las cuatro trampas del desplazamiento + +Acá es donde el `<<` y el `>>` dejan de ser "gratis". Leé esta lista dos veces. + +**Trampa 1 — corrimiento por el ancho o más es indefinido.** Correr `k >= ancho` (por ejemplo +`x << 32` sobre un `uint32_t`) es **comportamiento indefinido**: el estándar no dice qué pasa y cada +CPU puede dar algo distinto. Corré **siempre** con `0 <= k < ancho`. + +**Trampa 2 — `>>` de un valor con signo es dependiente de la implementación.** Para valores **sin +signo**, `>>` rellena con ceros (es "lógico"). Para valores **con signo negativos**, el estándar deja +que el compilador elija: la mayoría copia el bit de signo (corrimiento **aritmético**, `-8 >> 1` da +`-4`), pero **no está garantizado**. Regla: desplazá a la derecha **valores sin signo**. + +**Trampa 3 — multiplicar puede desbordar silenciosamente.** `5 << 30` sobre un `uint32_t` no da +`5 × 2³⁰`: los bits que se salen se pierden. El desplazamiento **no avisa** del desborde. + +**Trampa 4 — `<<` de un valor con signo que desborda es indefinido.** Mover a la izquierda un +`signed` de forma que el resultado no entre en el tipo es UB. Por eso, otra vez: **bits se manipulan +en `unsigned`**. + +> **¿Entonces el truco de ×2 es inútil?** No. Es **rapidísimo** y lo usan a fondo los compiladores. +> Vos, además, lo vas a necesitar para leer y escribir **campos** en formatos binarios (Parte 5), +> donde el desplazamiento no es una optimización sino la forma correcta de pararse sobre los bits. + +## Práctica de la Parte 4 + +### Ejercicio 17.8 — El multiplicador sin `*` + +**Conceptos** · `<<`, `>>`, multiplicación y división por potencias de dos + +**Contexto** · Los procesadores antiguos (y algunos chips chicos de hoy) no tenían multiplicador de +hardware: multiplicaban a fuerza de sumar y correr bits. Simplificación: multiplicás y dividís +**solo** con desplazamientos. + +**Consigna** · Dado un `uint32_t` `x` y un exponente `k` (de `0` a `30`), calculá `x * 2ᵏ` y +`x / 2ᵏ` **usando únicamente** `<<` y `>>` (nada de `*`, `/` ni `pow`). Mostrá el resultado en +decimal y binario. Comprobá con `k = 0` (no cambia nada) y con un `k` que haga perder bits. + +**Entrada** · Un valor `x` y un exponente `k`. +**Salida** · `x * 2ᵏ` y `x / 2ᵏ`, en decimal y binario. + +**Escenario de prueba** + +```text +x = 5, k = 3 +x * 2^3 = 40 (0b00000000000000000000000000101000) +x / 2^3 = 0 (5 >> 3, trunca a 0) + +x = 40, k = 2 +x * 2^2 = 160 +x / 2^2 = 10 +``` + +> **Ojo:** `5 / 8` "de verdad" es `0.625`, pero con enteros y `>>` da **0**. La división con `>>` +> **trunca**. + +**Identificadores sugeridos** · `value`, `shift`, `multiplyByShift`, `divideByShift` +**Criterios de aceptación** +- [ ] No se usa `*`, `/` ni `pow` en ninguna parte. +- [ ] `k = 0` devuelve el mismo valor. +- [ ] La división trunca hacia abajo (como `/` entre enteros). +- [ ] Se usa `uint32_t` (sin signo) para que `>>` sea confiable. + +--- + +### Ejercicio 17.9 — Empaquetar dos valores en uno + +**Conceptos** · `<<`, `|`, `&`, máscaras, empaquetado + +**Contexto** · A veces dos datos chicos tienen que viajar juntos dentro de uno más grande (por +ejemplo, dos coordenadas de 16 bits en un solo `uint32_t`). Simplificación: empaquetás y +desempaquetás dos valores. + +**Consigna** · Tomá dos `uint16_t` (`high` y `low`) y **empaquetálos** en **un** `uint32_t` con el +`high` en los 16 bits de arriba y el `low` en los 16 de abajo, usando `<<` y `|`. Después +**desempaquetálos** de vuelta con `>>` y `&`, y comprobá que recuperás los valores originales. + +**Entrada** · Dos valores de 16 bits. +**Salida** · El paquete de 32 bits (en hex y binario) y los dos valores recuperados. + +**Escenario de prueba** + +```text +high = 0xABCD low = 0x1234 +paquete = 0xABCD1234 +recuperado high = 0xABCD +recuperado low = 0x1234 +``` + +**Identificadores sugeridos** · `pack`, `unpackHigh`, `unpackLow`, `packed` +**Criterios de aceptación** +- [ ] El paquete se arma con `<<` y `|` (sin sumas ni multiplicaciones). +- [ ] `high` se extrae con `>>`; `low`, con una máscara de 16 bits. +- [ ] Los valores recuperados coinciden **exactamente** con los originales. +- [ ] Se comprueba que `high` y `low` no se pisaron. + +--- + +# PARTE 5 — `BYTE`, `WORD`, `DWORD`, `QWORD` y los paquetes de bits + +Ya viste que una caja puede tener 8, 32 o 64 bits. En el mundo de Windows (y en el de los archivos +binarios, como el BMP del capítulo `15`) esas cajas tienen **nombres clásicos**. Conocerlos te permite +leer APIs y formatos de archivo sin traducir mentalmente todo el tiempo. + +## 5.1 Los nombres de los anchos + +| Nombre | Bits | Bytes | Equivalente portable (``) | De dónde viene | +|---|---|---:|---|---| +| **BYTE** | 8 | 1 | `uint8_t` | `` | +| **WORD** | 16 | 2 | `uint16_t` | `` | +| **DWORD** | 32 | 4 | `uint32_t` | `` (*double word*) | +| **QWORD** | 64 | 8 | `uint64_t` | convención (*quad word*) | + +> **¿Qué significa "word"?** Originalmente, la **palabra** (*word*) era el tamaño con el que la CPU +> trabajaba de una: en las CPUs de 16 bits, una *word* eran 16 bits. De ahí el nombre. Con el tiempo +> las CPUs crecieron, y aparecieron *double word* (32) y *quad word* (64). Hoy "word" es un nombre +> **histórico**: por eso `DWORD` son 32 bits aunque tu máquina sea de 64. + +> **La versión portable existe y es mejor.** Si no estás en Windows, o querés código que compile en +> cualquier lado, usá `uint8_t` / `uint16_t` / `uint32_t` / `uint64_t` del capítulo `16`. `DWORD` es +> un `typedef` de 32 bits **atado a ``**. El **ancho** es lo importante; el nombre es +> cosmético. + +## 5.2 Un `DWORD` no es "un número": es una caja de 32 bits + +La clave de esta parte es cambiar el chip. Un `DWORD` (o un `uint32_t`) puede ser **dos cosas**: + +- **un número** (por ejemplo, una suma, un contador), o +- **una caja de 32 bits** donde cada tramo guarda un dato distinto. + +Cuando lo usás como caja, el **truco de máscaras y desplazamientos** es cómo entrás y salís de cada +tramo. Es lo mismo que hiciste en la Parte 4 con el paquete de 16+16, pero con más tabiques. + +## 5.3 Extraer campos: el patrón `(x >> shift) & mask` + +Para leer un campo de `w` bits que empieza en la posición `s`, el patrón es siempre el mismo: + +```text +campos = (packed >> s) & ((1u << w) - 1) +``` + +- `>> s` **trae** el campo hasta la posición 0. +- `(1u << w) - 1` es una **máscara de `w` unos** (por ejemplo, `w = 5` → `0b11111`), que **borra** + todo lo que no sea el campo. + +Ejemplo con los bytes de un color `0x00RRGGBB` (como en un BMP de 24 bits, capítulo `15`): + +```c +#include +#include + +int main(void) { + uint32_t color = 0x00336699; /* 00 RR GG BB */ + + /* Cada canal son 8 bits. Extraigo con >> y recorto con & 0xFF. */ + uint8_t r = (color >> 16) & 0xFF; /* 0x33 */ + uint8_t g = (color >> 8) & 0xFF; /* 0x66 */ + uint8_t b = (color ) & 0xFF; /* 0x99 */ + + printf("R=%02x G=%02x B=%02x\n", r, g, b); /* R=33 G=66 B=99 */ + + /* Y al revés: armar el color desde los canales (el | combina, el << posiciona). */ + uint32_t rebuilt = ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; + printf("rebuilt = 0x%06x\n", rebuilt); /* 0x336699 */ + return 0; +} +``` + +> **Pensalo como un edificio.** El `>>` es el **ascensor** que baja el campo hasta la planta baja; la +> máscara `& 0xFF` es el **portero** que deja pasar solo a los ocho bits de ese campo y echa al resto. +> Ese par —**bajar y filtrar**— es el 90% de leer formatos binarios. + +## 5.4 Empaquetar campos: el patrón `| (v << s)` + +Armar el valor es el espejo: se **sube** cada campo con `<<` a su posición y se **combina** todo con +`|`. Es exactamente lo que ya viste en 5.3, y lo que hace la función que arma el color. + +> **Un detalle que salva horas.** Cuando un campo tiene **1 bit**, es una bandera (Parte 3). Cuando +> tiene **varios bits**, es un **número** dentro de la caja. Antes de escribir una máscara, preguntate +> cuántos bits tiene el campo: un campo de 3 bits (por ejemplo, un nivel de `0` a `7`) necesita +> máscara `0b111`, no `0b1`. + +## Práctica de la Parte 5 + +### Ejercicio 17.10 — El decodificador de registros + +**Conceptos** · `uint32_t` (`DWORD`), extracción de campos, `>>`, `&`, máscara de `w` bits, `|` + +**Contexto** · Los protocolos de red y los archivos binarios empaquetan varios campos en un solo +entero (a veces de tamaño fijo, como un `DWORD`). Simplificación: un registro de 32 bits con cuatro +campos de distinto ancho que tenés que leer. + +**Consigna** · Definí un registro de **32 bits** con esta distribución: + +```text + 31 24 23 16 15 12 11 0 +┌────────────────┬──────────┬──────┬───────────┐ +│ versión (8) │ flags (8)│ tipo │ valor (12) │ +│ │ │ (4) │ │ +└────────────────┴──────────┴──────┴───────────┘ +``` + +Implementá: + +1. una función que **arme** el registro a partir de los cuatro valores (con `<<` y `|`); +2. cuatro funciones que **extraigan** cada campo (con `>>` y máscara de `w` bits); +3. una función que, dado el registro, imprima los cuatro campos. + +Comprobá que armar y extraer son inversos: el valor que metiste es el que sale. + +**Entrada** · Cuatro valores (versión `0`–`255`, flags `0`–`255`, tipo `0`–`15`, valor `0`–`4095`). +**Salida** · El registro en hex y los cuatro campos extraídos. + +**Escenario de prueba** + +```text +version = 2, flags = 0x84, type = 5, value = 2730 +registro = 0x02845AAA (hex) +version = 2 +flags = 0x84 +type = 5 +value = 2730 +``` + +> **Cómo comprobar el hex:** `version << 24` = `0x02000000`, `flags << 16` = `0x00840000`, +> `type << 12` = `0x00005000`, `value` = `0x00000AAA`. Sumados con `|`: `0x02845AAA`. + +**Identificadores sugeridos** · `packRecord`, `getVersion`, `getFlags`, `getType`, `getValue` +**Criterios de aceptación** +- [ ] Ningún campo se solapa con otro (cada uno tiene sus bits). +- [ ] Las máscaras se construyen como `(1u << w) - 1` (por ejemplo, `type` con 4 bits → `0xF`). +- [ ] El registro se maneja como `uint32_t` (el bit 31 no es signo). +- [ ] Armar y extraer devuelven exactamente los valores de entrada. + +--- + +# PARTE 6 — Conceptos extra, de fácil a difícil + +Todo lo de abajo es de la misma familia: operar bits. Están **ordenados por dificultad**, para que +vayas sumando herramientas. + +## 6.1 (Fácil) Limpiar el bit más bajo: `x & (x - 1)` + +`x & (x - 1)` **apaga el bit `1` más a la derecha** que tenga `x`. Es la forma canónica de recorrer +solo los bits encendidos: repetís esta operación y cada vuelta consume un bit. + +```text + x = 0b10110000 + x - 1 = 0b10101111 + x & (x-1) = 0b10100000 ← se apagó el bit 4 (el más bajo encendido) +``` + +## 6.2 (Fácil) ¿Es potencia de dos? `x && !(x & (x - 1))` + +Un número es potencia de dos si tiene **exactamente un bit** en `1`. Combinando 6.1: +`x && !(x & (x - 1))` es verdadero solo para `1, 2, 4, 8, …`. El `x &&` inicial excluye el `0`, que +técnicamente apagaría un bit que no existe. + +## 6.3 (Media) Aislar el bit más bajo: `x & -x` + +`x & -x` **deja solo** el bit `1` más a la derecha y apaga el resto. Funciona gracias al complemento a +dos (Parte 1). Es la base de estructuras de datos como el *Fenwick tree*. + +```text + x = 0b10110100 + -x = 0b01001100 (complemento a dos) + x & -x = 0b00000100 ← quedó solo el bit 2 +``` + +## 6.4 (Media) Contar bits encendidos: *popcount* + +**Popcount** es cuántos bits valen `1`. El camino a pie usa un bucle con `x &= x - 1` (6.1), que da +una vuelta por cada bit encendido. Pero desde **C23** hay soporte **estándar** en ``: + +```c +#include /* C23 */ + +unsigned n = 0b10110100u; +unsigned ones = stdc_count_ones(n); /* 4 */ +``` + +> **Antes de C23** se usaban extensiones del compilador: `__builtin_popcount(x)` (GCC/Clang). No son +> estándar, pero están por todos lados. + +## 6.5 (Media) Intercambiar sin variable temporal (XOR *swap*) + +El XOR tiene la propiedad de que `a ^ b ^ b == a`. Encadenando tres XOR se intercambian dos variables +**sin** una tercera: + +```text + a = a ^ b; + b = a ^ b; /* (a^b)^b = a */ + a = a ^ b; /* (a^b)^a = b */ +``` + +> **Ojo.** Es un truco **didáctico**, no una buena práctica: con la variable temporal el compilador +> hace lo mismo o mejor, y el XOR-swap **falla si `a` y `b` son la misma variable** (se anula a cero). +> Conocelo por cultura, no lo uses en producción. + +## 6.6 (Media) Rotar: correr sin perder bits + +Con `<<` y `>>` los bits que salen se **pierden**. **Rotar** es un desplazamiento circular: lo que +sale por un lado **entra por el otro**. No hay operador estándar (los procesadores tienen `ROL`/`ROR`, +no C), así que se combina un `<<` y un `>>`: + +```text + rotar 8 bits a la izquierda en 1: + 0b10000001 → 0b00000011 (el 1 que salió por la izquierda reaparece a la derecha) +``` + +C23 también lo trae en ``: `stdc_rotate_left(x, n)` y `stdc_rotate_right(x, n)`. + +## 6.7 (Difícil) Endianness: ¿por qué `0x1234` no siempre se guarda igual? + +**Endianness** (el "orden de los bytes") es cómo se **almacenan** los bytes de un número multi-byte en +memoria: + +- **Little-endian** (x86, ARM en su modo normal): el byte **menos** significativo va **primero**. El + `uint32_t 0x12345678` se guarda como los bytes `78 56 34 12`. +- **Big-endian** (muchas redes, el protocolo TCP/IP): el byte **más** significativo va **primero**: + `12 34 56 78`. + +```c +#include +#include + +int main(void) { + uint32_t n = 0x12345678; + /* Miramos los bytes crudos del entero, uno por uno (unsigned char sí puede aliasar). */ + unsigned char *p = (unsigned char *)&n; + for (size_t i = 0; i < sizeof n; i++) { + printf("%02x ", p[i]); + } + printf("\n"); /* little-endian: 78 56 34 12 | big-endian: 12 34 56 78 */ + return 0; +} +``` + +> **Por qué te importa.** Si leés un `DWORD` de un archivo o de la red en una máquina del signo +> opuesto, los **bytes vienen al revés**. Por eso existen `htonl`/`ntohl` (host-to-network) y por eso +> los formatos de archivo **fijan** su endianness en la especificación (el BMP del capítulo `15` es +> little-endian). Los bits **dentro** de un byte, en cambio, no cambian: el desorden es **entre +> bytes**. + +## 6.8 (Difícil) `bitfields`: dejar que C haga las máscaras por vos + +En vez de escribir `>>` y `&` a mano, C tiene una forma declarativa de empaquetar campos dentro de un +`struct`: los **campos de bits** (*bit-fields*). Después de los `:` va cuántos bits tiene el campo. + +```c +#include + +/* Un struct de 1 byte (según el compilador) repartido en 3 campos. */ +struct Color5 { + unsigned r : 5; /* 5 bits: 0..31 */ + unsigned g : 6; /* 6 bits: 0..63 */ + unsigned b : 5; /* 5 bits: 0..31 */ +}; + +int main(void) { + struct Color5 c = { 12, 40, 20 }; + printf("r=%u g=%u b=%u\n", c.r, c.g, c.b); + printf("sizeof = %zu\n", sizeof c); /* típicamente 4 (con relleno) */ + return 0; +} +``` + +> **El lado oscuro de los bitfields.** El **orden** de los bits dentro del `struct` **no está +> garantizado** por el estándar: cambia entre compiladores y plataformas. Además, meten relleno +> (capítulo `12`) que arruina el cálculo. Por eso, **para leer un formato binario externo usá `>>` y +> `&`**, no bitfields. Los bitfields sirven cuando el "paquete" es **interno** y no sale nunca de tu +> programa. + +## 6.9 (Difícil) Uniones para reinterpretar bits: *type punning* + +Una **unión** (`union`) hace que varios tipos **compartan la misma memoria**. Escribís como uno y leés +como otro: eso se llama *type punning*. + +```c +#include +#include + +union Word { + uint32_t full; /* vista como número de 32 bits */ + uint8_t bytes[4]; /* la MISMA memoria vista como 4 bytes */ +}; + +int main(void) { + union Word w; + w.full = 0x11223344; + /* en little-endian, w.bytes[] = { 0x44, 0x33, 0x22, 0x11 } */ + printf("%02x %02x %02x %02x\n", w.bytes[0], w.bytes[1], w.bytes[2], w.bytes[3]); + return 0; +} +``` + +> **Con cuidado.** Reinterpretar por unión es **dependiente de la implementación** (funciona en GCC, +> Clang y MSVC, pero el estándar no lo garantiza al 100%). La forma **segura** de reinterpretar bytes +> sin violar el *aliasing* es `memcpy`, con `unsigned char` (como en 6.7). Conocelo, pero preferí +> `memcpy` para código serio. + +## Práctica de la Parte 6 + +### Ejercicio 17.11 — La caja de herramientas de bits + +**Conceptos** · `x & (x-1)`, potencia de dos, `x & -x`, popcount, tipos sin signo + +**Contexto** · Los trucos de bits son herramientas sueltas que conviene tener probadas. Simplificación: +una librería de funciones que recibe un número y responde preguntas sobre sus bits. + +**Consigna** · Implementá, con `uint32_t`: + +1. `countBits(x)`: cuántos bits en `1` tiene (contalos recorriendo con `x &= x - 1`); +2. `isPowerOfTwo(x)`: verdadero solo si `x` es una potencia de dos (`1, 2, 4, 8, …`); +3. `lowestBit(x)`: el **valor** del bit encendido más bajo (con `x & -x`); +4. `highestBit(x)`: la **posición** del bit encendido más alto. + +Probá cada una con varios valores, incluido `0` y un valor con un solo bit (por ejemplo `1u << 31`). + +**Entrada** · Varios valores de 32 bits. +**Salida** · La respuesta de cada función para cada valor. + +**Escenario de prueba** + +```text +x = 0b10110100 (180) + countBits = 4 + isPowerOfTwo= no + lowestBit = 4 (0b100) + highestBit = 7 (bit 7 = 128) + +x = 0b01000000 (64) + countBits = 1 + isPowerOfTwo= sí + lowestBit = 64 + highestBit = 6 + +x = 0 + countBits = 0 + isPowerOfTwo= no + lowestBit = 0 +``` + +**Identificadores sugeridos** · `countBits`, `isPowerOfTwo`, `lowestBit`, `highestBit` +**Criterios de aceptación** +- [ ] Todo se hace con operadores de bits y tipos **sin signo**. +- [ ] `isPowerOfTwo(0)` devuelve **falso** (ojo con el `0`). +- [ ] El `1u << 31` se maneja bien (no aparece como negativo). +- [ ] Los resultados del escenario salen exactos. + +--- + +### Ejercicio 17.12 — El ejercicio jefe: un archivo de paquetes binarios + +**Conceptos** · todo lo anterior — `uintN_t`, máscaras, `<<`, `>>`, `&`, `|`, endianness, archivos + +**Contexto** · Un sistema de telemetría escribe cada medición como un **DWORD** de 32 bits donde se +empaquetan varios campos. Para que este ejercicio entre en el capítulo, podés mantener los registros +en un **arreglo** en memoria (sin archivos); si querés, guardalos con `fwrite` (capítulo `15`) como +extra. + +**Consigna** · Definí el siguiente registro de 32 bits: + +```text + 31 20 19 16 15 4 3 0 +┌────────────────┬────────┬───────────┬──────────┐ +│ sensorId (12) │ temp │ presion │ status │ +│ │ (4) │ (12) │ (4) │ +└────────────────┴────────┴───────────┴──────────┘ +``` + +1. Escribí una función que **empaquete** los cuatro campos en un `uint32_t`. +2. Escribí funciones que **extraigan** cada campo. +3. Cargá un arreglo con **al menos cinco** mediciones (con valores a elección, incluyendo `temp` + negativa codificada en 4 bits con complemento a dos, y ambos extremos de `sensorId`). +4. Mostrá cada medición con sus cuatro campos **desempaquetados**. +5. Añadí una función que **cuente cuántas mediciones** tienen el `status` con el bit 0 encendido. + +**Entrada** · Un arreglo de registros `uint32_t` (a elección). +**Salida** · Cada registro en hex con sus campos, y el conteo final. + +**Escenario de prueba** + +```text +registro = 0x054A4A32 + sensorId = 0x054 (84) + temp = 0xA (4 bits, complemento a dos = -6) + presion = 0x4A3 (1187) + status = 0x2 + +... (cuatro mediciones más) + +mediciones con status bit0 = 3 +``` + +> **Cómo comprobar el hex del escenario:** `sensorId << 20` = `0x05400000`, +> `temp << 16` = `0x000A0000`, `presion << 4` = `0x00004A30`, `status` = `0x00000002`. Con `|`: +> `0x054A4A32`. Y `0xA` en 4 bits con signo es `-6` (complemento a dos, Parte 1). + +**Identificadores sugeridos** · `TelemetryRecord`, `packRecord`, `getSensorId`, `getTemp`, +`getPressure`, `getStatus`, `countWithBit0` +**Criterios de aceptación** +- [ ] Las máscaras se construyen con `(1u << w) - 1`, nunca "a ojo". +- [ ] `temp` se extrae como número **con signo** de 4 bits (ver Parte 1) y puede dar negativo. +- [ ] Todos los campos se recuperan sin pisarse entre sí. +- [ ] El conteo del `status & 1` es correcto. +- [ ] Se usa `uint32_t` (el bit 31 no es signo). + +--- + +## 17.13 Errores comunes + +- **Confundir `&` con `&&`** (y `|` con `||`): los primeros operan bit a bit; los segundos, sobre la + verdad completa. +- **Testear una bandera con `==`** en vez de `&`. +- **Confundir "alguna" con "todas"** al testear un grupo: `flags & GRUPO` vs `(flags & GRUPO) == GRUPO`. +- **Desplazar por el ancho o más** (`x << 32`): comportamiento indefinido. +- **Desplazar a la derecha un `signed` negativo**: dependiente de la implementación. +- **Operar bits con tipos con signo**: usá `unsigned`. +- **Enteros multi-byte crudos**: acordate de la **endianness** al leer archivos o redes. +- **Bitfields para formatos externos**: el orden de bits no es portable; usá `>>` y `&`. + +--- + +## 17.14 Cierre + +Bajaste un piso y ya no ves números: ves **filas de interruptores**. Con cuatro operadores (`&`, `|`, +`^`, `~`) y dos desplazamientos (`<<`, `>>`) tenés todo lo necesario para: + +- meter **ocho banderas en un byte** (y 32 en un `DWORD`) en vez de ocho `bool`; +- **testear, prender, apagar e invertir** cada bandera con una máscara; +- **multiplicar y dividir** por potencias de dos corriendo bits; +- **leer y escribir campos** dentro de un `uint32_t` (`BYTE`/`WORD`/`DWORD`/`QWORD`) con el par + `>>` y `&`; +- entender por qué el mismo `0xFF` es `255` o `-1`, y por qué un `0x1234` no siempre se guarda igual + (endianness). + +**Regla mental.** Cada vez que veas un montón de datos que son "sí/no" repetidos, un conjunto de +opciones, o un formato binario con campos, preguntate: **"¿esto es un número, o es una caja de bits?"**. +Si es una caja de bits, ya sabés abrirla. + +**Resumen de la caja de herramientas.** + +| Quiero… | Escribo… | +|---|---| +| Fabricar la máscara del bit `n` | `1u << n` | +| Prender el bit `n` | `x \|= (1u << n)` | +| Apagar el bit `n` | `x &= ~(1u << n)` | +| Invertir el bit `n` | `x ^= (1u << n)` | +| Testear el bit `n` | `x & (1u << n)` | +| Máscara de `w` bits | `(1u << w) - 1` | +| Extraer un campo | `(x >> s) & ((1u << w) - 1)` | +| Armar un campo | `x \|= (v & ((1u << w) - 1)) << s` | +| Multiplicar por `2ᵏ` | `x << k` | +| Dividir por `2ᵏ` (trunca) | `x >> k` | +| Apagar el bit más bajo | `x & (x - 1)` | +| Aislar el bit más bajo | `x & -x` | +| ¿Potencia de dos? | `x && !(x & (x - 1))` | +| Contar bits en 1 (C23) | `stdc_count_ones(x)` | + +> **El puente al capítulo siguiente.** Ahora que ves los bits, el mundo binario se te abre: leer +> imágenes (BMP, capítulo `15`), escribir compresores, entender protocolos de red y hasta mirar cómo +> un `float` guarda su signo, su exponente y su mantisa. Todo eso son **máscaras y desplazamientos** +> aplicados a `uintN_t`. Lo difícil ya lo tenés. diff --git a/dist/17-bits-y-mascaras.pdf b/dist/17-bits-y-mascaras.pdf new file mode 100644 index 0000000..557cba4 Binary files /dev/null and b/dist/17-bits-y-mascaras.pdf differ