Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -30,6 +30,8 @@ Usar el MCP **`md-to-pdf`** → herramienta **`md-to-pdf_markdown_to_pdf`**.
|
|||||||
| `footerText` | el capítulo, p. ej. `12 — Arenas` |
|
| `footerText` | el capítulo, p. ej. `12 — Arenas` |
|
||||||
|
|
||||||
3. Comprobar que la respuesta diga `PDF generated: ... (<n> KB)`.
|
3. Comprobar que la respuesta diga `PDF generated: ... (<n> KB)`.
|
||||||
|
4. Verificar que **ningún título quede huérfano** (sin sus tres líneas de contenido al pie). Si
|
||||||
|
aparece uno, forzar el salto de página descrito en "Reglas de diseño" y regenerar.
|
||||||
|
|
||||||
## Reglas de diseño (no romper)
|
## Reglas de diseño (no romper)
|
||||||
|
|
||||||
@@ -43,6 +45,12 @@ Usar el MCP **`md-to-pdf`** → herramienta **`md-to-pdf_markdown_to_pdf`**.
|
|||||||
distintos de nota.
|
distintos de nota.
|
||||||
- **Resaltado**: `md-to-pdf` usa `highlight.js` y emite clases `.hljs-*`; los colores están en
|
- **Resaltado**: `md-to-pdf` usa `highlight.js` y emite clases `.hljs-*`; los colores están en
|
||||||
`print.css`. No cambiar colores fuera de `print.css`.
|
`print.css`. No cambiar colores fuera de `print.css`.
|
||||||
|
- **Títulos sin huérfanos**: un título (`h1`–`h4`) **nunca** puede quedar al pie de página: siempre
|
||||||
|
tiene que arrastrar **al menos tres líneas de contenido** debajo, en la misma hoja. Lo garantiza
|
||||||
|
`print.css` (`break-after: avoid` en `h1`–`h4` y en el primer párrafo que los sigue, más
|
||||||
|
`orphans`/`widows: 3`). Si aun así un título cae solo al final de una página, **forzar un salto
|
||||||
|
de página antes de ese título** con un bloque HTML `<div style="break-before: page;"></div>`,
|
||||||
|
y no dejar el título huérfano.
|
||||||
|
|
||||||
## Cambiar el diseño
|
## Cambiar el diseño
|
||||||
|
|
||||||
|
|||||||
@@ -196,6 +196,11 @@ Reglas de diseño (resumen):
|
|||||||
(las trazas de consola van como ```` ```text ````).
|
(las trazas de consola van como ```` ```text ````).
|
||||||
- **Notas / advertencias:** se escriben como **blockquote** (`>`). En el PDF **todas** se dibujan
|
- **Notas / advertencias:** se escriben como **blockquote** (`>`). En el PDF **todas** se dibujan
|
||||||
igual, con un **fondo de color** que resalta del blanco de la hoja.
|
igual, con un **fondo de color** que resalta del blanco de la hoja.
|
||||||
|
- **Títulos sin huérfanos:** un título (`h1`–`h4`) **nunca** puede quedar solo al pie de página:
|
||||||
|
siempre tiene que llevar **al menos tres líneas de contenido** debajo, en la misma hoja. Si no
|
||||||
|
caben, se fuerza un **salto de página** antes del título. `print.css` ya lo aplica
|
||||||
|
(`break-after: avoid` en los títulos y en el primer párrafo que los sigue, más `orphans`/`widows: 3`);
|
||||||
|
no romper esas reglas.
|
||||||
- `printBackground` debe estar **activo** (si no, no se pintan los fondos de código y notas).
|
- `printBackground` debe estar **activo** (si no, no se pintan los fondos de código y notas).
|
||||||
- Encabezado, pie de página y **numeración de páginas** fijos.
|
- Encabezado, pie de página y **numeración de páginas** fijos.
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,32 @@ h2 { font-size: 16pt; margin-top: 1.7em; border-bottom: 1px solid var(--rule); p
|
|||||||
h3 { font-size: 13pt; margin-top: 1.3em; }
|
h3 { font-size: 13pt; margin-top: 1.3em; }
|
||||||
h4 { font-size: 11.5pt; margin-top: 1.1em; }
|
h4 { font-size: 11.5pt; margin-top: 1.1em; }
|
||||||
|
|
||||||
|
/* --- Control de saltos de página ---
|
||||||
|
Evita que un título quede solo al pie de una página. No se corta DESPUÉS del
|
||||||
|
título, y el primer párrafo que lo acompaña también arrastra lo que sigue, así
|
||||||
|
el título se lleva al menos un par de líneas de contenido real. */
|
||||||
|
h1, h2, h3, h4 {
|
||||||
|
break-after: avoid;
|
||||||
|
page-break-after: avoid;
|
||||||
|
}
|
||||||
|
h2 + p, h3 + p, h4 + p {
|
||||||
|
break-after: avoid;
|
||||||
|
page-break-after: avoid;
|
||||||
|
}
|
||||||
|
h2, h3, h4 { break-inside: avoid; }
|
||||||
|
|
||||||
|
/* Que listas, código, tablas y notas no se partan (salvo que no quepan en una hoja) */
|
||||||
|
ul, ol, pre, blockquote, table { break-inside: avoid; }
|
||||||
|
|
||||||
|
/* Que un bloque de código o una tabla no se separe de la línea que lo introduce */
|
||||||
|
pre, table { break-before: avoid; }
|
||||||
|
|
||||||
|
/* Un párrafo nunca deja menos de 3 líneas de un lado del salto */
|
||||||
|
p, li, blockquote {
|
||||||
|
orphans: 3;
|
||||||
|
widows: 3;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- Código inline: MONO, distinta de la serif del texto --- */
|
/* --- Código inline: MONO, distinta de la serif del texto --- */
|
||||||
code {
|
code {
|
||||||
font-family: "Cascadia Code", "JetBrains Mono", "Consolas", "Courier New", monospace;
|
font-family: "Cascadia Code", "JetBrains Mono", "Consolas", "Courier New", monospace;
|
||||||
|
|||||||
@@ -0,0 +1,507 @@
|
|||||||
|
# Capítulo 13 — Tipos más usados de C
|
||||||
|
|
||||||
|
> Capítulo avanzado. Se apoya en tipos primitivos (`02`), operadores (`03`), arreglos (`06`),
|
||||||
|
> punteros (`07`) y `struct` (`08`).
|
||||||
|
>
|
||||||
|
> Recorre los tipos "con nombre" más usados de C —`size_t`, `wchar_t`, `NULL`, `bool`, los enteros de
|
||||||
|
> ancho exacto y los caracteres Unicode— y de qué cabecera sale cada uno. El objetivo es que sepas
|
||||||
|
> **cuándo NO alcanza con un `int`**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13.1 ¿Por qué no alcanza con un `int`?
|
||||||
|
|
||||||
|
Un `int` resuelve muchísimos casos, pero **no responde de forma portable** tres preguntas:
|
||||||
|
|
||||||
|
1. **¿Cuánto pesa esto?** → `sizeof` no devuelve un `int`, devuelve `size_t`.
|
||||||
|
2. **¿Cuántos bits ocupa este número?** → para garantizar ese tamaño están `intN_t` / `uintN_t`.
|
||||||
|
3. **¿Cuál es el carácter más grande que puedo guardar?** → un `char` (1 byte) no alcanza; hay
|
||||||
|
`wchar_t` y `char32_t`.
|
||||||
|
|
||||||
|
> **¿Qué es el "ancho" de un tipo?** Es **cuántos bits ocupa el número en memoria**. `int32_t`
|
||||||
|
> ocupa siempre 32 bits (4 bytes) en cualquier máquina; `int`, en cambio, mide 32 bits en la mayoría
|
||||||
|
> de los equipos de hoy, pero 16 en otros. Cuando el tamaño **tiene** que ser siempre el mismo —un
|
||||||
|
> archivo, un protocolo de red, hardware— no confíes en `int`: usá `int32_t` (con signo) o `uint32_t`
|
||||||
|
> (sin signo), donde ese número **es** el ancho en bits.
|
||||||
|
|
||||||
|
La respuesta de C fueron **typedefs con nombre** que se comportan igual en cualquier plataforma.
|
||||||
|
Casi ninguno es palabra del lenguaje: son **alias que aporta una cabecera**, y por eso importa saber
|
||||||
|
**de dónde sale cada uno**.
|
||||||
|
|
||||||
|
> **La idea central.** No elegís `size_t` ni `uint32_t` por capricho: son los tipos que la
|
||||||
|
> implementación **garantiza** que representan cierto valor en *cualquier* máquina. `int` y `long`
|
||||||
|
> **no** dan esa garantía: cambian de tamaño según la plataforma.
|
||||||
|
|
||||||
|
### Los modelos de datos: por qué cambian los tamaños
|
||||||
|
|
||||||
|
El estándar **no fija** que `int` mida 4 bytes; solo exige un orden creciente
|
||||||
|
(`char ≤ short ≤ int ≤ long ≤ long long`). Las decisiones reales de cada sistema son el **modelo de
|
||||||
|
datos** (valores en bits):
|
||||||
|
|
||||||
|
| Tipo | LP32 | ILP32 | LLP64 | LP64 |
|
||||||
|
|---|---:|---:|---:|---:|
|
||||||
|
| `int` | 16 | **32** | **32** | **32** |
|
||||||
|
| `long` | 32 | 32 | 32 | **64** |
|
||||||
|
| puntero / `size_t` | 32 | 32 | **64** | **64** |
|
||||||
|
|
||||||
|
- **ILP32**: Windows y Unix de 32 bits. **LLP64**: **Windows 64** (`long` sigue en 32, punteros 64).
|
||||||
|
- **LP64**: **Linux y macOS 64** (`long` y punteros en 64). **LP32**: Windows de 16 bits (histórico).
|
||||||
|
|
||||||
|
> **Lo que te tiene que quedar grabado:** en 64 bits, un `int` mide **32 bits** pero un puntero mide
|
||||||
|
> **64**. Por eso `int` no puede guardar una dirección, ni un tamaño mayor a 2 GiB. Ese es el motivo
|
||||||
|
> de que existan `size_t` y los tipos de ancho fijo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13.2 El mapa de los tipos
|
||||||
|
|
||||||
|
Casi ningún tipo "con nombre" es palabra del lenguaje: son **typedefs** de una cabecera. Estos son
|
||||||
|
los que vas a usar todo el tiempo (tamaño típico en un sistema de 64 bits):
|
||||||
|
|
||||||
|
| Tipo | Bytes | Para qué |
|
||||||
|
|---|---:|---|
|
||||||
|
| `char`, `int`, `long`, `float`, `double`, … | — | los primitivos de siempre |
|
||||||
|
| `size_t` | 8 | tamaños y conteos |
|
||||||
|
| `wchar_t` | 2 / 4 | carácter ancho |
|
||||||
|
| `NULL` | = puntero | puntero nulo |
|
||||||
|
| `int8_t` … `int64_t` / `uint8_t` … `uint64_t` | 1 / 2 / 4 / 8 | entero de ancho **exacto** |
|
||||||
|
| `bool` | 1 | verdadero / falso |
|
||||||
|
| `char16_t` / `char32_t` | 2 / 4 | Unicode UTF-16 / UTF-32 |
|
||||||
|
| `char8_t` | 1 | Unicode UTF-8 |
|
||||||
|
| `float16_t` … `float128_t` | 2 / 4 / 8 / 16 † | flotante de ancho **exacto** |
|
||||||
|
|
||||||
|
> **†** Solo desde **C23** (`<stdfloat.h>`) y **opcional**: flotantes de ancho exacto (16 / 32 / 64 /
|
||||||
|
> 128 bits) que en el C clásico (C99–C17) no existen.
|
||||||
|
|
||||||
|
> **Regla práctica.** Incluí **la cabecera del tipo que usás**: si usás `uint32_t`, incluí
|
||||||
|
> `<stdint.h>`; si usás `size_t`, `<stddef.h>`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13.3 `size_t` — los tamaños
|
||||||
|
|
||||||
|
**Cabecera:** `<stddef.h>`
|
||||||
|
|
||||||
|
Entero **sin signo**. Es el tipo del resultado de `sizeof`, el que devuelven `strlen` y `fread`, y el
|
||||||
|
que reciben `malloc` y `memcpy`. Mide **4 bytes** en 32 bits y **8** en 64. Su máximo es `SIZE_MAX`
|
||||||
|
(`<stdint.h>`).
|
||||||
|
|
||||||
|
**En vez de `int`.** Guardar un tamaño en un `int` **desborda** si pasa de ~2 GiB. Y comparar un
|
||||||
|
`int` con un `size_t` es una trampa silenciosa: el `int` se convierte a sin signo.
|
||||||
|
|
||||||
|
```c
|
||||||
|
int i = -1;
|
||||||
|
size_t n = 5;
|
||||||
|
if (i < n) { /* NO entra: -1 se volvió 18446744073709551615 (SIZE_MAX) */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cuidado.** `size_t` no tiene signo: `size_t x = 5; x -= 10;` da `SIZE_MAX - 4`, no `-5`. Nunca lo
|
||||||
|
uses en un `for` inverso (`for (size_t i = n - 1; i >= 0; i--)` es **infinito**). **Se imprime con
|
||||||
|
`%zu`.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13.4 `wchar_t` — caracteres anchos
|
||||||
|
|
||||||
|
**Cabecera:** `<stddef.h>`
|
||||||
|
|
||||||
|
Un `char` ocupa 1 byte: alcanza para `A` o `z`, pero **no** para `ñ`, `€` ni `😀`. Para poder
|
||||||
|
guardar cualquier carácter existe un tipo **"ancho"**: `wchar_t`.
|
||||||
|
|
||||||
|
*Wide* significa "ancho": un carácter ancho ocupa **más de un byte** (2 o 4), así que entra completo
|
||||||
|
en un solo `wchar_t`. Para escribir uno se antepone una `L`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
char c = 'A'; /* 1 byte */
|
||||||
|
wchar_t wc = L'ñ'; /* carácter ancho: lleva el prefijo L */
|
||||||
|
wchar_t *ws = L"corazón"; /* cadena ancha: cada carácter, un wchar_t */
|
||||||
|
```
|
||||||
|
|
||||||
|
Y así como `strlen` recorre una cadena normal, **`wcslen`** recorre una cadena ancha (`wchar_t *`).
|
||||||
|
|
||||||
|
| Plataforma | `wchar_t` | Codificación |
|
||||||
|
|---|---:|---|
|
||||||
|
| **Windows** | **2 bytes** | UTF-16 |
|
||||||
|
| **Linux / macOS** | **4 bytes** | UTF-32 |
|
||||||
|
|
||||||
|
> El **mismo** `wchar_t` mide 2 bytes en Windows y 4 en Linux: código que asume uno u otro se rompe
|
||||||
|
> al portarlo. Para Unicode **portable** preferí `char32_t` (`<uchar.h>`), cuyo ancho no depende de
|
||||||
|
> la plataforma. Sus primos: `char16_t` (UTF-16) y `char8_t` (UTF-8, C23).
|
||||||
|
|
||||||
|
**En vez de `int`.** Meter el carácter en un `int` tampoco arregla nada: `int` no es un tipo de
|
||||||
|
carácter y confunde. Y `wchar_t` tampoco tiene ancho fijo (2 o 4 bytes, ver la tabla), así que la
|
||||||
|
clave no es "agrandar" el tipo, sino **elegir el correcto**. **Se imprimen con `%lc` (carácter) y
|
||||||
|
`%ls` (cadena), previo `setlocale`.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13.5 `NULL` — el puntero nulo
|
||||||
|
|
||||||
|
**Cabecera:** `<stddef.h>`
|
||||||
|
|
||||||
|
`NULL` es una macro que expone una constante de puntero nulo (típicamente `((void*)0)`). Representa
|
||||||
|
"este puntero no apunta a nada": es lo que devuelve `malloc` al fallar y contra lo que se compara.
|
||||||
|
|
||||||
|
**En vez de `int` (y sin mezclar).** No confundas tres cosas distintas: `'\0'` (carácter nulo, para
|
||||||
|
cadenas), `0` (número, para enteros) y `NULL` (puntero). `int x = NULL;` compila, pero es un error
|
||||||
|
conceptual.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13.6 `bool` — verdadero o falso
|
||||||
|
|
||||||
|
**Cabecera:** `<stdbool.h>` (C99; en C23, palabra clave)
|
||||||
|
|
||||||
|
`bool` es un macro que expande a `_Bool` (C99), junto con `true` y `false`. Ocupa **1 byte**. En
|
||||||
|
**C23** son palabras clave y ya no hace falta la cabecera.
|
||||||
|
|
||||||
|
**En vez de `int`.** `int flag = 1;` no dice qué es ese `1`; `bool done = true;` sí. Además la
|
||||||
|
conversión es distinta: `(bool)0.5` es **verdadero**, mientras que `(int)0.5` es `0`. **Cuándo NO
|
||||||
|
usarlo:** si necesitás más de dos estados, un `enum` expresa mejor la intención.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13.7 Enteros de ancho exacto
|
||||||
|
|
||||||
|
**Cabecera:** `<stdint.h>`
|
||||||
|
|
||||||
|
Estos resuelven el "¿`int` o `long`?" de una vez: su **número de bits es exacto** y **no cambia**
|
||||||
|
entre plataformas. Son los preferidos para protocolos, archivos binarios y hardware.
|
||||||
|
|
||||||
|
| Tipo | Bytes | Mínimo | Máximo |
|
||||||
|
|---|---:|---:|---:|
|
||||||
|
| `int8_t` | 1 | -128 | **127** |
|
||||||
|
| `uint8_t` | 1 | 0 | **255** |
|
||||||
|
| `int16_t` | 2 | -32 768 | **32 767** |
|
||||||
|
| `uint16_t` | 2 | 0 | **65 535** |
|
||||||
|
| `int32_t` | 4 | -2 147 483 648 | **2 147 483 647** |
|
||||||
|
| `uint32_t` | 4 | 0 | **4 294 967 295** |
|
||||||
|
| `int64_t` | 8 | -9 223 372 036 854 775 808 | **9 223 372 036 854 775 807** |
|
||||||
|
| `uint64_t` | 8 | 0 | **18 446 744 073 709 551 615** |
|
||||||
|
|
||||||
|
> Los tipos **sin signo** arrancan en `0` y llegan hasta `2ⁿ − 1`. Los **con signo** reparten esos
|
||||||
|
> bits: la mitad para negativos, por eso `int8_t` llega a `127` (no a `128`). Cada máximo tiene su
|
||||||
|
> nombre en `<stdint.h>`: `INT8_MAX`, `UINT8_MAX`, `INT64_MAX`, `UINT64_MAX`, …
|
||||||
|
|
||||||
|
**En vez de `int`.** Un `int` mide 4 bytes en 64 bits, pero **2** en LP32: no garantiza 32 bits.
|
||||||
|
`int32_t` sí. Para **imprimir** uno de estos tipos, convertí explícitamente a un tipo estándar de
|
||||||
|
igual o mayor ancho, así el formato coincide:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
uint64_t big = 123456789012345ULL;
|
||||||
|
printf("%llu\n", (unsigned long long)big);
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Ojo:** `uint8_t` y `unsigned char` casi siempre miden 1 byte, pero no son lo mismo. `uint8_t` es
|
||||||
|
> para **bytes de datos**; `unsigned char`, para **memoria cruda**. Y `intN_t` es **opcional** en el
|
||||||
|
> estándar: existe solo si la plataforma tiene ese ancho exacto.
|
||||||
|
|
||||||
|
> **¿Y los `float`?** En el C clásico (C99–C17) **no** hay `intN_t` para la coma flotante: `float`,
|
||||||
|
> `double` y `long double` miden 4, 8 y 8 o 16 bytes según la máquina, sin garantía del estándar. Solo
|
||||||
|
> desde **C23** existe `<stdfloat.h>` con `float16_t`, `float32_t`, `float64_t` y `float128_t` (los
|
||||||
|
> `_FloatN`): el equivalente de `intN_t`. Son **nuevos y opcionales**, mucho menos portables.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13.8 ¿Tengo que dejar de usar `int`?
|
||||||
|
|
||||||
|
**No.** Estos tipos **no reemplazan** a `int`: lo **complementan** para casos puntuales. El `int`
|
||||||
|
sigue siendo el tipo por defecto para números enteros comunes.
|
||||||
|
|
||||||
|
Seguí usando **`int`** salvo que tengas un motivo concreto: contadores, cuentas, índices chicos,
|
||||||
|
resultados de operaciones, o valores que le pasás a funciones que ya devuelven o reciben `int`
|
||||||
|
(`getchar`, `strcmp`, `printf`).
|
||||||
|
|
||||||
|
Pasá a un **tipo preciso** solo cuando **exista un motivo**:
|
||||||
|
|
||||||
|
- **`size_t`** → tamaños y conteos: `sizeof`, `strlen`, índices de arreglos, `malloc`.
|
||||||
|
- **`intN_t` / `uintN_t`** → el ancho **tiene** que ser exacto: archivos binarios, protocolos de red,
|
||||||
|
registros de hardware, o datos que deben ocupar lo mismo en cualquier máquina.
|
||||||
|
- **`bool`** → una respuesta sí/no.
|
||||||
|
- **`wchar_t` / `char32_t`** → un carácter que no entra en un `char`.
|
||||||
|
|
||||||
|
> **Y ojo con el revés:** usar un tipo chico "por las dudas" también trae problemas. `int8_t` llega
|
||||||
|
> hasta 127 (ver la tabla de 13.7): si lo usás para contar, se desborda enseguida. Y encima la
|
||||||
|
> aritmética se hace en `int` igual.
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* MAL: contar hasta 1000 con un int8_t (máximo 127) */
|
||||||
|
int8_t i = 0;
|
||||||
|
for (i = 1; i <= 1000; i++) { /* i nunca pasa de 127: el bucle se rompe */ }
|
||||||
|
|
||||||
|
/* BIEN: un int común alcanza de sobra */
|
||||||
|
for (int i = 1; i <= 1000; i++) { /* ... */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include <stdint.h>
|
||||||
|
int8_t a = 100, b = 100;
|
||||||
|
int c = a + b; /* 200: la suma se calcula en int, y al achicar se desborda */
|
||||||
|
```
|
||||||
|
|
||||||
|
| Situación | Tipo |
|
||||||
|
|---|---|
|
||||||
|
| Un número entero común (contar, sumar, cuentas) | `int` |
|
||||||
|
| Tamaño o conteo (`sizeof`, `strlen`, `malloc`) | `size_t` |
|
||||||
|
| Posición en un arreglo | `size_t` |
|
||||||
|
| Dato de ancho exacto (archivo, protocolo, hardware) | `intN_t` / `uintN_t` |
|
||||||
|
| Una condición (sí / no) | `bool` |
|
||||||
|
| Un carácter fuera de `char` | `char32_t` / `wchar_t` |
|
||||||
|
|
||||||
|
> **Regla corta.** `int` por defecto. Cambiá de tipo **solo cuando tengas un motivo** —ancho exacto,
|
||||||
|
> tamaño, signo o un carácter—. Si no hay motivo, quedate con `int`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13.9 ¿Los tipos chicos ahorran memoria?
|
||||||
|
|
||||||
|
**Depende de dónde.** Guardar **muchos** valores en tipos chicos sí ahorra memoria; una variable
|
||||||
|
suelta, no.
|
||||||
|
|
||||||
|
- **Una variable sola:** no ahorra. El compilador la guarda en un registro o le reserva un espacio
|
||||||
|
alineado, así que `int8_t i;` e `int i;` terminan ocupando lo mismo. Y encima la aritmética se
|
||||||
|
hace en `int`.
|
||||||
|
- **Un arreglo grande:** sí, mucho. Un millón de elementos:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include <stdint.h>
|
||||||
|
int8_t small[1000000]; /* 1 000 000 bytes (≈ 1 MB) */
|
||||||
|
int large[1000000]; /* 4 000 000 bytes (≈ 4 MB) */
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Como campos de una `struct`:** puede achicar la estructura (el tipo chico ocupa menos), aunque
|
||||||
|
el padding puede comerse parte del ahorro. Conviene medir con `sizeof`.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include <stdint.h>
|
||||||
|
struct Color1 { uint8_t r, g, b; }; /* 3 bytes */
|
||||||
|
struct Color2 { int r, g, b; }; /* 12 bytes */
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Cuándo vale la pena un tipo chico.** Cuando guardás **muchos** valores y cada byte cuenta:
|
||||||
|
> imágenes, audio, sensores, buffers leídos de un archivo o de la red, tablas grandes. Ahí `uint8_t`
|
||||||
|
> o `int16_t` hacen una diferencia real.
|
||||||
|
|
||||||
|
> **Lo que NO te da:** velocidad. La CPU trabaja con palabras (`int`), así que operar con tipos
|
||||||
|
> chicos suele ser igual o más lento (hay que extender el signo). El motivo para usarlos es
|
||||||
|
> **memoria** (y el ancho exacto), no rendimiento.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13.10 Errores comunes
|
||||||
|
|
||||||
|
- Comparar un `int` negativo con `sizeof` o `strlen` (el `-1` se vuelve `SIZE_MAX`).
|
||||||
|
- Guardar un tamaño (`sizeof`, tamaño de un archivo) en un `int`.
|
||||||
|
- `for (size_t i = n - 1; i >= 0; i--)`: nunca termina.
|
||||||
|
- Asumir `sizeof(wchar_t)`, o que `int` == `int32_t`.
|
||||||
|
- Imprimir `size_t` con `%d` en vez de `%zu`.
|
||||||
|
- Confundir `'\0'`, `0` y `NULL`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13.11 Ejercicios
|
||||||
|
|
||||||
|
> De **noob** a **pro**, con dificultad creciente. Cada ejercicio declara sus conceptos objetivo;
|
||||||
|
> resolvelos **usando esos conceptos**. Trae un **escenario de prueba** (entrada/salida esperada,
|
||||||
|
> nunca el algoritmo) para que compares lo que imprimís. Compilá con un estándar moderno (C11 o C23)
|
||||||
|
> y avisos activos (`-Wall -Wextra`).
|
||||||
|
|
||||||
|
### Ejercicio 13.1 — Radiografía de tipos
|
||||||
|
|
||||||
|
**Conceptos** · `sizeof`, `size_t`, `printf` (`%zu`), tipos de `<stddef.h>` y `<stdint.h>`
|
||||||
|
|
||||||
|
**Contexto** · Vas a portar un programa a otra máquina y querés saber, antes de confiar en él, qué
|
||||||
|
tamaño tiene cada tipo. Simplificación: solo imprimís tamaños.
|
||||||
|
|
||||||
|
**Consigna** · Imprimí una tabla con el tamaño en bytes de: `char`, `int`, `long`, `void *`,
|
||||||
|
`size_t`, `wchar_t`, `bool`, `int32_t` y `uint8_t`. Usá `%zu` para el tamaño e incluí la cabecera de
|
||||||
|
cada tipo.
|
||||||
|
|
||||||
|
**Entrada** · Ninguna. **Salida** · Una fila por tipo, con nombre y tamaño.
|
||||||
|
|
||||||
|
**Escenario de prueba** ·
|
||||||
|
```text
|
||||||
|
LP64 (Linux/macOS 64-bit) LLP64 (Windows 64-bit)
|
||||||
|
int .......... 4 int .......... 4
|
||||||
|
long ......... 8 <-- long ......... 4 <-- ¡ojo!
|
||||||
|
void* ........ 8 void* ........ 8
|
||||||
|
size_t ....... 8 size_t ....... 8
|
||||||
|
wchar_t ...... 4 <-- wchar_t ...... 2 <-- ¡ojo!
|
||||||
|
bool ......... 1 bool ......... 1
|
||||||
|
int32_t ...... 4 (siempre) int32_t ...... 4 (siempre)
|
||||||
|
uint8_t ...... 1 (siempre) uint8_t ...... 1 (siempre)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Identificadores sugeridos** · `sizeOfTypes`, `printSize`
|
||||||
|
**Criterios de aceptación** ·
|
||||||
|
- Cada tamaño sale de un `size_t` y se imprime con `%zu`.
|
||||||
|
- Compila **sin** avisos de conversión.
|
||||||
|
- Se explica en un comentario por qué `long` y `wchar_t` pueden diferir entre plataformas.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ejercicio 13.2 — Sumar con `size_t`
|
||||||
|
|
||||||
|
**Conceptos** · `size_t`, arreglos, `for`, casos borde
|
||||||
|
|
||||||
|
**Contexto** · Un carrito suma precios que ya vienen en un arreglo. Simplificación: no hay entrada
|
||||||
|
interactiva.
|
||||||
|
|
||||||
|
**Consigna** · Escribí una función que reciba un arreglo de `int` y su **cantidad** (`size_t`) y
|
||||||
|
devuelva la suma. Recorré con un índice `size_t`. Con cantidad `0`, devolvé `0` sin leer fuera.
|
||||||
|
|
||||||
|
**Entrada** · Una cantidad y esa cantidad de valores. **Salida** · La suma.
|
||||||
|
|
||||||
|
**Escenario de prueba** ·
|
||||||
|
```text
|
||||||
|
count = 3 ; [10, 20, 30] -> 60
|
||||||
|
count = 0 ; (sin valores) -> 0 (no se accede a memoria)
|
||||||
|
count = 1 ; [-5] -> -5
|
||||||
|
```
|
||||||
|
|
||||||
|
**Identificadores sugeridos** · `sumPrices`, `count`, `index`
|
||||||
|
**Criterios de aceptación** ·
|
||||||
|
- El índice y la cantidad son `size_t` (no `int`).
|
||||||
|
- `count == 0` devuelve `0` sin desreferenciar nada.
|
||||||
|
- No hay conversiones de `size_t` a `int`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ejercicio 13.3 — El índice que se convierte en gigante
|
||||||
|
|
||||||
|
**Conceptos** · conversiones con/sin signo, `size_t`, comparaciones
|
||||||
|
|
||||||
|
**Contexto** · Revisión de código: un bucle compara un `int` contra una cantidad `size_t` y, cuando
|
||||||
|
el índice llega a `-1`, "funciona" de forma inesperada. Simplificación: se reproduce a mano.
|
||||||
|
|
||||||
|
**Consigna** · Compará un `int` que valga `-1` contra un `size_t` que valga `5` y mostrá que `-1 < 5`
|
||||||
|
da **falso**. Luego escribí la versión correcta con `size_t` y explicá en un comentario por qué.
|
||||||
|
|
||||||
|
**Entrada** · `i = -1`, `n = 5`. **Salida** · El resultado de la comparación y la explicación.
|
||||||
|
|
||||||
|
**Escenario de prueba** ·
|
||||||
|
```text
|
||||||
|
(int -1) < (size_t 5) -> falso
|
||||||
|
explicación: -1 convertido a size_t = SIZE_MAX (18446744073709551615), y SIZE_MAX < 5 es falso.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Identificadores sugeridos** · `index`, `count`
|
||||||
|
**Criterios de aceptación** ·
|
||||||
|
- Se reproduce el comportamiento sorprendente.
|
||||||
|
- La explicación menciona la conversión a `SIZE_MAX`.
|
||||||
|
- La versión corregida no depende de conversiones implícitas.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ejercicio 13.4 — Texto que no entra en un byte
|
||||||
|
|
||||||
|
**Conceptos** · `wchar_t`, `L""`, `%lc`, `%ls`, `<uchar.h>`
|
||||||
|
|
||||||
|
**Contexto** · Un cartel muestra en consola el nombre de una ciudad con acentos. Simplificación:
|
||||||
|
guardás y mostrás caracteres anchos.
|
||||||
|
|
||||||
|
**Consigna** · Guardá un carácter no ASCII (por ejemplo `ñ` o `€`) en un `wchar_t` y una cadena con
|
||||||
|
acentos en un `wchar_t *`. Imprimilos con los especificadores correctos. Además, imprimí
|
||||||
|
`sizeof(wchar_t)` y explicá por qué para Unicode **portable** conviene `char32_t` en lugar de
|
||||||
|
`wchar_t`.
|
||||||
|
|
||||||
|
**Entrada** · Uno o más caracteres anchos. **Salida** · El carácter y la cadena, más el tamaño.
|
||||||
|
|
||||||
|
**Escenario de prueba** ·
|
||||||
|
```text
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
wchar_t c = L'ñ'; wchar_t *s = L"corazón";
|
||||||
|
c = ñ ; s = corazón
|
||||||
|
sizeof(wchar_t) = 2 en Windows (UTF-16) / 4 en Linux (UTF-32)
|
||||||
|
contraste: guardar 'ñ' en un char y usar %c da basura o un byte suelto.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Identificadores sugeridos** · `wideChar`, `wideString`, `wideSize`
|
||||||
|
**Criterios de aceptación** ·
|
||||||
|
- Se usa `wchar_t` para el carácter y la cadena, con `%lc` y `%ls`.
|
||||||
|
- Se configura el locale antes de imprimir.
|
||||||
|
- Se explica por qué un `char` no alcanza y por qué `char32_t` es más portable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ejercicio 13.5 — El entero de ancho exacto (pro)
|
||||||
|
|
||||||
|
**Conceptos** · `<stdint.h>`, tipos de ancho exacto, portabilidad
|
||||||
|
|
||||||
|
**Contexto** · Leés un header binario de red cuyos campos tienen anchos **exactos** (8, 16, 32, 64
|
||||||
|
bits). Un `int` te haría depender de la plataforma. Simplificación: escribís y leés los valores, sin
|
||||||
|
red real.
|
||||||
|
|
||||||
|
**Consigna** · Definí un pequeño "paquete" con campos `uint8_t`, `uint16_t`, `uint32_t` y `uint64_t`.
|
||||||
|
Asignales valores, imprimilos convirtiendo explícitamente a un tipo estándar de ancho suficiente (por
|
||||||
|
ejemplo `unsigned long long` para el de 64 bits) y comprobá que sobreviven. Explicá por qué `int32_t`
|
||||||
|
en lugar de `int`.
|
||||||
|
|
||||||
|
**Entrada** · Valores de distinto ancho. **Salida** · Cada campo impreso, sin avisos.
|
||||||
|
|
||||||
|
**Escenario de prueba** ·
|
||||||
|
```text
|
||||||
|
kind = 0xAB (uint8_t) -> 171
|
||||||
|
length = 4900 (uint16_t) -> 4900
|
||||||
|
seq = 3000000000U (uint32_t) -> 3000000000
|
||||||
|
total = 123456789012345ULL (uint64_t) -> 123456789012345
|
||||||
|
```
|
||||||
|
|
||||||
|
**Identificadores sugeridos** · `Packet`, `kind`, `length`, `seq`, `total`
|
||||||
|
**Criterios de aceptación** ·
|
||||||
|
- Los tipos son de ancho exacto (`uint8_t` … `uint64_t`).
|
||||||
|
- Cada valor se imprime con un formato que coincide con su tipo (sin warnings).
|
||||||
|
- Se explica por qué `int32_t` es mejor que `int` para datos de ancho fijo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13.12 Cierre
|
||||||
|
|
||||||
|
Los tipos de C no son un adorno: son la forma de decir **qué querés representar** y **de qué
|
||||||
|
tamaño**. `size_t` para tamaños, `uintN_t`/`intN_t` para ancho exacto, `bool` para verdades,
|
||||||
|
`wchar_t`/`char32_t` para texto ancho y `NULL` para el puntero vacío. Cada uno vive en **su
|
||||||
|
cabecera**, y elegir el tipo (y la cabecera) correctos es la mitad del camino hacia código portable.
|
||||||
|
|
||||||
|
Regla mental: cada vez que escribas `int`, preguntate **"¿este valor podría superar `INT_MAX`, venir
|
||||||
|
de un `sizeof`, o necesitar un ancho exacto?"**. Si la respuesta es sí, hay un tipo mejor que `int`
|
||||||
|
— y ahora sabés cuál es y dónde vive.
|
||||||
|
|
||||||
|
**Resumen: cada tipo y su cabecera.**
|
||||||
|
|
||||||
|
| Tipo | Cabecera |
|
||||||
|
|---|---|
|
||||||
|
| `size_t`, `wchar_t`, `NULL` | `<stddef.h>` |
|
||||||
|
| `intN_t` / `uintN_t` | `<stdint.h>` |
|
||||||
|
| `bool` | `<stdbool.h>` |
|
||||||
|
| `char16_t`, `char32_t`, `char8_t` | `<uchar.h>` |
|
||||||
|
| `int`, `long`, …, `INT_MAX` | `<limits.h>` |
|
||||||
|
| `float`, `double`, …, `FLT_MAX` | `<float.h>` |
|
||||||
|
| `float16_t`…`float128_t` † | `<stdfloat.h>` |
|
||||||
|
|
||||||
|
> **†** `float16_t`…`float128_t` son de **C23** (`<stdfloat.h>`) y **opcionales**: no están en el C
|
||||||
|
> clásico ni los trae cualquier compilador.
|
||||||
|
|
||||||
|
> **¿Alguno de estos viene solo, sin incluirlo?** A veces sí, de yapa. Como `<stdio.h>` **usa**
|
||||||
|
> `size_t` (por ejemplo, en `fread`) y devuelve `NULL` cuando algo falla (por ejemplo, `fopen`), esa
|
||||||
|
> cabecera está **obligada a traer** `size_t` y `NULL`. Así que si ya incluiste `<stdio.h>`,
|
||||||
|
> `<stdlib.h>` o `<string.h>`, esos dos ya están disponibles.
|
||||||
|
>
|
||||||
|
> **Pero no te acostumbres a la yapa.** El estándar solo te garantiza un tipo si incluís **su**
|
||||||
|
> cabecera; cuáles trae de arrastre cada header cambia de compilador en compilador. Regla de oro:
|
||||||
|
> **incluí siempre la cabecera del tipo que usás.** Si tu programa compila "de casualidad" porque
|
||||||
|
> otro header lo trajo, se rompe el día que cambies de compilador.
|
||||||
|
|
||||||
|
Así se ve la buena costumbre:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include <stdio.h> /* para printf */
|
||||||
|
#include <stdint.h> /* para uint32_t: su cabecera */
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
uint32_t n = 42;
|
||||||
|
printf("%u\n", (unsigned)n);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Reference in New Issue
Block a user