diff --git a/AGENTS.md b/AGENTS.md index f92d2e5..cc86be8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -145,7 +145,63 @@ Un ejercicio puede combinar conceptos de niveles anteriores, pero **no debe exig --- -## 7. Lo que está prohibido +## 7. Estilo didáctico al explicar conceptos ("para tontos") + +Los capítulos no solo enuncian ejercicios: también **explican** los conceptos que el lector necesita. +Esa explicación sigue un estilo llano, concreto y progresivo —el mismo que se usó en +`book/14-entrada-salida-estandar.md`—, pensado para que el lector **entienda de verdad** y no solo +lea. Estas reglas aplican a **todo capítulo** que explique conceptos. + +**Analogía cotidiana primero.** Todo concepto arranca con una comparación del mundo real que el lector +ya domina; recién después aparece el término técnico. Ejemplos del capítulo 14: las tres tuberías +(`stdin`/`stdout`/`stderr`) son "una fábrica"; `getchar` es "sacar caramelos de a uno"; el búfer es +"el villano de la película". La analogía se sostiene durante toda la explicación; no se abandona a la +primera frase. + +**Títulos con gancho, no etiquetas.** En vez de "Introducción a `getchar`", el título promete algo +concreto: "`getchar`: el caramelito de a uno". Lo mismo vale para las subsecciones. + +**Explicar el POR QUÉ, no solo el qué.** Cada regla llega con su motivo. No alcanza con decir "guardá +en `int`, no en `char`": hay que explicar por qué (tiene que representar los 256 bytes posibles **y** +un valor extra, `EOF`). + +**Voz cercana y frases cortas.** Se trata al lector de "vos" (ver sección 6). Párrafos breves, en +presente, sin rodeos. Se permite —y se espera— marcar la idea clave con **negrita** y con MAYÚSCULAS +ocasionales. + +**Cómo estructurar un capítulo largo:** + +- Dividirlo en **partes** con un **índice** al inicio. La tabla lleva, por cada parte, "la idea en una + frase". +- Después de cada concepto, un **bloque de práctica** con uno o más ejercicios cortos, antes de + avanzar al siguiente concepto. +- Cerrar con un apartado tipo "lo que te llevás": las ideas clave en pocos bullets, y el puente al + capítulo siguiente. + +**Recursos que se repiten:** + +- **Apodos para las trampas** clásicas: "la trampa N.º 1", "el bug más famoso del principiante", "el + villano de la película". +- **Blockquotes** (`>`) para notas, advertencias y "momentos ajá": "Momento ajá", "Ojo con esto", + "Para guardar", "Truco de laboratorio". En el PDF todas se dibujan con fondo de color (sección 10). +- **Tablas comparativas** para contrastar opciones o señales parecidas (p. ej. `EOF` vs `'\0'` vs + `'\n'`). +- **Diagramas ASCII** para mostrar lo visual: flujos conectados, los dos búferes en serie. +- **Código ilustrativo comentado en lenguaje llano**: cada línea que introduce algo nuevo lleva un + comentario claro y **de varias líneas** que dice exactamente qué hace. Los bloques declaran su + lenguaje en el fence para que `highlight.js` los resalte (sección 10). +- **Escenario de prueba** en cada ejercicio: un bloque `text` con la entrada y la salida esperada, + para que el lector pueda **comprobar** su solución. + +**El límite que NO se cruza.** Este estilo permite **código ilustrativo para explicar un concepto** +(una llamada a una función, un esqueleto mínimo, un patrón genérico), pero **nunca** la solución de un +ejercicio. Las reglas no negociables (sección 2) siguen mandando: se entrega el **problema**; la +lógica que resuelve la consigna la escribe el lector. El código de ejemplo sirve para **entender** una +herramienta, jamás para resolver un enunciado del libro. + +--- + +## 8. Lo que está prohibido - Escribir la solución, total o parcial, del enunciado (incluida la lógica en pseudocódigo). - Convertir el ejercicio en un proyecto grande, con build system, múltiples archivos o dependencias externas. @@ -156,7 +212,7 @@ Un ejercicio puede combinar conceptos de niveles anteriores, pero **no debe exig --- -## 8. Flujo para agregar contenido +## 9. Flujo para agregar contenido Checklist al crear o editar un ejercicio: @@ -175,7 +231,7 @@ Checklist al crear o editar un ejercicio: --- -## 9. Generación de PDF (`dist/`) +## 10. Generación de PDF (`dist/`) Cada capítulo trabajado se publica como **PDF** en `dist/`, con el **mismo nombre** que su Markdown: `book/12-arenas.md` → `dist/12-arenas.pdf`. diff --git a/book/13-argumentos-de-linea-de-comandos.md b/book/13-argumentos-de-linea-de-comandos.md new file mode 100644 index 0000000..b8f108a --- /dev/null +++ b/book/13-argumentos-de-linea-de-comandos.md @@ -0,0 +1,1444 @@ +# Capítulo 13 — Argumentos de línea de comandos: de cero a pro + +> Capítulo intermedio, pero **escrito para que se entienda de verdad**. Se apoya en variables (`02`), +> operadores (`03`), `if` y bucles (`04`), funciones (`05`), arreglos y cadenas (`06`) y punteros (`07`). +> +> Es la **antesala** del capítulo de entrada/salida estándar (`14-entrada-salida-estandar`). Acá +> aprendés a que tu programa reciba **órdenes al nacer** (cuando lo ejecutás) en lugar de pedirlas +> recién mientras corre. En el capítulo siguiente vas a profundizar en las tres tuberías +> (`stdin`/`stdout`/`stderr`), que acá tocamos apenas lo justo. + +--- + +## Cómo leer este capítulo + +Hasta ahora, todos tus programas empezaban **preguntando**. Le mostraban un cartel al usuario y +esperaban a que escribiera algo por teclado. Eso funciona, pero no es la única forma. Las herramientas +serias —`ls`, `grep`, `git`, `gcc`— no te preguntan nada: **les pasás las órdenes en el mismo momento +en que las ejecutás**. + +Ese es el tema de este capítulo. + +Está dividido en **nueve partes**. Después de cada idea importante hay una **práctica** —uno o varios +ejercicios cortos— para que la uses ya mismo, antes de avanzar. **No saltees las prácticas**: son la +mitad del capítulo. + +| Parte | De qué habla | La idea en una frase | +|---|---|---| +| **1** | Órdenes al nacer | Tu programa puede recibir los datos cuando lo ejecutás, no después. | +| **2** | `argc` y `argv` | El par mágico que `main` recibe con la cantidad y la lista de argumentos. | +| **3** | `argv[0]` | El primer argumento siempre es **cómo llamaron** a tu programa. | +| **4** | Recorrer y clasificar | Cómo leer los argumentos uno por uno y distinguir qué tipo es cada uno. | +| **5** | Opciones cortas y largas | `-v` vs `--verbose`: dos estilos que conviven en toda herramienta. | +| **6** | Opciones con valor | `--dato=2` o `--dato 2`: cómo leer el valor que acompaña a una opción. | +| **7** | Validar y avisar | Errores por `stderr`, códigos de salida y un `--help` digno. | +| **8** | `getopt` | El parseo ordenado de opciones cortas, con soporte para `-abc` apiladas. | +| **9** | `getopt_long` | El nivel pro: opciones largas al estilo `--output=archivo`. | + +Los **ejercicios** se numeran en orden: `13.1`, `13.2`, `13.3`, y así. Cada uno trae su **escenario de +prueba** (qué entra y qué tiene que salir) para que puedas **comprobar** tu solución. + +> **Regla de oro del libro:** el capítulo explica y muestra **cómo funciona** cada cosa, pero **los +> ejercicios no traen la solución**. El enunciado te da el problema; la respuesta la escribís vos. + +--- + +# PARTE 1 — Tu programa puede recibir órdenes al nacer + +Antes de tocar una sola línea de código, entendamos **qué cambia** y **por qué** esto es tan útil. + +## 1.1 La diferencia entre preguntar y que te digan + +Pensá en una **máquina expendedora**. + +- **Forma antigua (preguntar durante la ejecución):** prendés la máquina y ella te va preguntando: + *"¿Qué querés?"*. Vos respondés por un teclado mientras la máquina ya está funcionando. En C, eso es + usar `scanf`, `getchar` o `fgets` sobre `stdin`: el programa **se frena y espera**. +- **Forma nueva (recibir órdenes al nacer):** **le das los ingredientes ANTES de encenderla**. Le decís: + *"2 latas de Coca-Cola"*. La ejecutás y ya sabe qué hacer: **no pregunta, va al resultado**. + +Eso es exactamente lo que pasa cuando escribís en la terminal: + +```bash +./programa test.txt --alguna-opcion --dato=2 +``` + +Le estás pasando **órdenes al programa en el mismo momento en que lo ejecutás**. Son los **argumentos +de línea de comandos** (en inglés, *command-line arguments*). + +> **Momento ajá.** La diferencia clave es el **momento**: los argumentos llegan **al arrancar el +> programa** (cuando empieza `main`), no durante su ejecución. + +## 1.2 ¿Por qué esto importa? + +Porque es **el** mecanismo con el que se automatiza trabajo en la vida real. + +- Un script de respaldo hace `./backup --origen=/data --destino=/backup --comprimir`. +- Un analizador de logs hace `./analiza server.log --desde=2026-01-01 --nivel=error`. +- El compilador hace `gcc -o salida programa.c -Wall`. + +Nadie está sentado respondiendo preguntas: los datos **viajan con la orden**. Eso permite que un +programa llame a otro, encadenarlos con tuberías y automatizarlos con tareas programadas. + +## 1.3 Lo que ya conocés, y lo que viene + +Ya sabés que `main` es la puerta de entrada del programa. Su forma mínima es: + +```c +int main(void) +{ + /* ... */ + return 0; +} +``` + +Ese `int` de adelante es el **código de retorno**: `0` significa "salió bien". Ya lo veníamos usando. + +Para recibir argumentos, `main` admite **dos parámetros extra**. Esa es toda la novedad: + +```c +int main(int argc, char *argv[]) +{ + /* ... */ + return 0; +} +``` + +No pasa nada raro: es la misma función `main`, con dos datos de entrada más. En la Parte 2 los +desarmamos pieza por pieza. + +## Práctica de la Parte 1 + +### Ejercicio 13.1 — Pensar antes de ejecutar + +**Conceptos** · argumentos de línea de comandos, diferencia con la lectura por teclado + +**Contexto** · Antes de escribir código, hay que entender **cuándo** llegan los datos. Simplificación: +dos formas de pedirle algo a un programa. + +**Consigna** · Leé estas dos escenas y respondé por escrito (sin programar): + +1. Ejecutás `./programa`, aparece el cartel `¿Archivo?` y vos escribís `datos.txt`. +2. Ejecutás `./programa datos.txt` directamente desde la terminal. + +¿En cuál de las dos el programa **ya sabe qué archivo usar desde el arranque**? ¿Cuál corresponde a +"argumentos de línea de comandos"? + +**Entrada** · Ninguna (es un ejercicio de análisis). +**Salida** · Una respuesta escrita comparando ambas formas. + +**Escenario de prueba** · No aplica (no hay programa). + +**Criterios de aceptación** +- [ ] Identificás que en la escena 2 los datos llegan **al ejecutar**. +- [ ] Explicás la diferencia entre pedir datos **durante** la ejecución y **al ejecutarla**. +- [ ] Nombrás correctamente la forma 2 como "argumentos de línea de comandos". + +--- + +# PARTE 2 — `argc` y `argv`: el par mágico + +## 2.1 `argc`: el contador + +Cuando ejecutás un programa desde la consola, el **sistema operativo** (a través del shell) le entrega +a `main` **cuántas** palabras escribiste y **cuáles** fueron. + +El primero de los dos parámetros es `argc`: + +- Significa **"argument count"**: *cantidad de argumentos*. +- Es un `int`. +- Cuenta **cuántas palabras** separadas por espacios llegaron en la línea de comandos. + +**Analogía.** Imaginá una lista de compras escrita en un papel. `argc` es **el número de ítems** del +papel. No te dice qué comprar: solo **cuántas cosas** hay. + +> **Para guardar.** `argc` **nunca es 0** en un programa ejecutado normalmente. Siempre cuenta al +> menos **1**. ¿Por qué? Lo vas a ver en la Parte 3 (spoiler: el nombre del programa también cuenta). + +## 2.2 `argv`: la lista + +El segundo parámetro es `argv`: + +- Significa **"argument vector"**: *vector (lista) de argumentos*. +- Es un **arreglo de cadenas**: `char *argv[]`. +- Cada posición guarda **una palabra** exactamente como apareció en la terminal. + +**Analogía.** Si `argc` es el número de ítems de la lista, `argv` es **la lista misma**: un estante +donde cada balda guarda una palabra completa. `argv[0]` es el primer estante, `argv[1]` el segundo, y +así hasta `argv[argc-1]`. + +> **Un detalle jurídico.** `argv` es un arreglo de **punteros a `char`** (`char *`). Cada elemento +> apunta al comienzo de una cadena terminada en `'\0'`. Por eso se escribe `char *argv[]` y no +> `char argv[]`: no guardamos las letras, guardamos **la dirección** de dónde empiezan. Si todavía +> tenés dudas con punteros, repasá el capítulo `07-punteros`. + +## 2.3 El ejemplo completo, palabra por palabra + +Si ejecutás: + +```bash +./programa test.txt --alguna-opcion --dato=2 +``` + +el sistema reparte los argumentos así: + +| Índice | Valor de `argv[i]` | ¿Qué es? | +|---|---|---| +| `0` | `"./programa"` | El nombre con el que se ejecutó. | +| `1` | `"test.txt"` | Un argumento pasado. | +| `2` | `"--alguna-opcion"` | Otro argumento. | +| `3` | `"--dato=2"` | Otro más. | + +Y `argc` vale **4** (hay 4 palabras). Fijate que `argc` **cuenta también el nombre**: por eso +`argv[0]` a `argv[3]` son 4 posiciones, y `argc = 4`. + +> **Ojo con esto.** El **espacio** es lo que separa un argumento del siguiente. Cada bloque entre +> espacios se convierte en un `argv[i]` distinto. Si querés que un texto con espacios sea **un solo** +> argumento, hay que **encerrarlo entre comillas**: `./programa "hola mundo"` llega como un único +> `argv[1]` igual a `"hola mundo"`. + +## 2.4 Diagrama mental + +```text + Línea: ./programa test.txt --alguna-opcion --dato=2 + │ │ │ │ + ▼ ▼ ▼ ▼ + argv ─► ┌────────┐┌─────────┐┌──────────────┐┌─────────┐ + │[0] ││[1] ││[2] ││[3] │ ... + │"./prog"││"test.txt"││"--alguna-op" ││"--dato=2"│ + └────────┘└─────────┘└──────────────┘└─────────┘ + argc = 4 (cuatro palabras, incluido el nombre) +``` + +## 2.5 El caso más chico posible + +Si ejecutás: + +```bash +./saludo +``` + +obtenés: + +- `argc = 1` +- `argv[0] = "./saludo"` + +Aunque **no pasaste nada extra**, `argc` vale 1: el nombre del programa **siempre** ocupa el estante 0. + +## Práctica de la Parte 2 + +### Ejercicio 13.2 — Contar y adivinar + +**Conceptos** · `argc`, `argv`, índice de argumentos + +**Contexto** · Antes de programar, hay que **leer** una línea de comandos como la leería el sistema. +Simplificación: adivinar valores sin ejecutar nada. + +**Consigna** · Dada la invocación: + +```bash +./programa archivo.txt -v 42 +``` + +respondé sin programar: + +1. ¿Cuánto vale `argc`? +2. ¿Qué contiene `argv[0]`, `argv[1]`, `argv[2]` y `argv[3]`? +3. ¿Por qué `argc` **no** vale 3? + +**Entrada** · La línea de comandos del enunciado. +**Salida** · Las tres respuestas. + +**Escenario de prueba** · No aplica (no hay programa). + +**Criterios de aceptación** +- [ ] `argc` correcto (recordá que el nombre cuenta). +- [ ] Los cuatro valores de `argv` correctos y en orden. +- [ ] Justificás el punto 3 mencionando que `argv[0]` es el nombre del programa. + +### Ejercicio 13.3 — Eco de argumentos + +**Conceptos** · `argc`, `argv`, bucle `for`, `%s` + +**Contexto** · El primer programa "de verdad" con argumentos: repetir lo que recibió. Simplificación: +un eco que no interpreta nada, solo muestra. + +**Consigna** · Escribí un programa que, al ejecutarlo, muestre por pantalla **la cantidad** de +argumentos y luego **cada argumento** en una línea, en el formato del escenario de prueba. Recorré el +arreglo con un bucle `for`. + +**Entrada** · Los argumentos que le pases al ejecutarlo. +**Salida** · Una línea con la cantidad y una línea por argumento, con el formato exacto del ejemplo. + +**Escenario de prueba** + +```text +$ ./eco hola mundo 123 +argc = 4 +argv[0] = ./eco +argv[1] = hola +argv[2] = mundo +argv[3] = 123 +``` + +**Identificadores sugeridos** · `argc`, `argv`, `i` + +**Restricciones / pistas** +- El bucle debe llegar **hasta `argc-1`**: nunca leas `argv[argc]` (estarías fuera del arreglo). +- Empezás en `i = 0` si querés mostrar **todos** (incluido el nombre). En el próximo ejercicio + empezarás en `i = 1`. + +**Criterios de aceptación** +- [ ] Muestra el valor correcto de `argc`. +- [ ] Recorre de `0` a `argc-1` con un `for`. +- [ ] El formato coincide exactamente con el escenario de prueba. +- [ ] No accede a ninguna posición fuera del rango válido. + +--- + +# PARTE 3 — `argv[0]`: el nombre del programa + +## 3.1 El argumento número cero es especial + +`argv[0]` guarda **cómo se llamó al programa** en esa ejecución concreta. No es un dato que pasaste +vos: es el nombre con el que el sistema lo invocó. + +**Analogía.** Es como presentarte: *"Hola, soy Horacio"*. `argv[0]` es **el nombre con el que te +llamaron** en ese momento, no quién sos en realidad. + +Puede ser una ruta relativa (`./programa`), absoluta (`/usr/bin/programa`) o, en Windows, incluir +`.exe` (`programa.exe`). Todo depende de **cómo lo escribiste al ejecutarlo**. + +## 3.2 ¿Para qué sirve saber el nombre? + +Para **mensajes honestos**. + +Cuando un programa necesita decirle al usuario cómo usarlo (un mensaje de "Uso: ..."), lo correcto es +mostrar **el mismo nombre con el que lo llamaron**, no uno fijo escrito a mano. + +- Si el usuario lo ejecutó como `./mini`, tu ayuda debería decir `Uso: ./mini ...`. +- Si lo ejecutó como `mini`, debería decir `Uso: mini ...`. + +**Nunca hardcodees el nombre del ejecutable.** Usá `argv[0]`. + +> **Momento ajá.** El truco `printf("Uso: %s \n", argv[0]);` es uno de los patrones más +> repetidos de la programación de consola. Guardátelo. + +## 3.3 Un caso de borde + +`argv[0]` **puede estar vacío** (`""`) en situaciones muy raras de algunos sistemas, pero en la +práctica casi nunca ocurre. Lo normal es que tenga el nombre, relativo o absoluto. + +## Práctica de la Parte 3 + +### Ejercicio 13.4 — El nombre con el que me llamaron + +**Conceptos** · `argv[0]`, cadenas, `printf` con `%s` + +**Contexto** · Todo programa de consola debería mostrar su propio nombre en las ayudas. Simplificación: +mostrar el nombre y una línea de uso genérica. + +**Consigna** · Escribí un programa que muestre su nombre (`argv[0]`) y un mensaje de uso que lo +incluya **dinámicamente**, con el formato del escenario de prueba. + +**Entrada** · Cualquier cosa (el programa no necesita argumentos extra). +**Salida** · Dos líneas con el formato exacto del ejemplo. + +**Escenario de prueba** + +```text +$ ./demo archivo.txt +Programa: ./demo +Uso: ./demo [opciones] +``` + +**Identificadores sugeridos** · `argv` + +**Restricciones / pistas** +- El nombre del programa **no** debe escribirse literal: usá `argv[0]`. +- Si lo ejecutás con otra ruta (`./carpeta/demo`), el mensaje debe reflejarlo. + +**Criterios de aceptación** +- [ ] Muestra `argv[0]` tal cual. +- [ ] El mensaje de uso usa `argv[0]`, no un texto fijo. +- [ ] Cambia correctamente si el programa se ejecuta con otra ruta. + +--- + +# PARTE 4 — Recorrer y clasificar argumentos + +## 4.1 El patrón para recorrer (sin salirse nunca) + +Para leer los argumentos que **le pasaste al programa** (no el nombre), se recorre desde `argv[1]` +**hasta `argv[argc-1]`**. La condición del bucle es **siempre** `i < argc`. + +```c +#include + +int main(int argc, char *argv[]) +{ + /* Recorro desde 1: el 0 (argv[0]) es el nombre del programa. */ + for (int i = 1; i < argc; i++) { + printf("Argumento %d: %s\n", i, argv[i]); + } + return 0; +} +``` + +- **Empezamos en 1** porque `argv[0]` es el nombre, no un dato de tu tarea. +- **Terminamos cuando `i == argc`** porque la última posición válida es `argc-1`. + +> **Trampa N.º 1 — salirse del arreglo.** Si la condición fuera `i <= argc`, en algún momento leerías +> `argv[argc]`, que **no existe**: es memoria basura. El resultado es un *segfault* o datos +> impredecibles. La condición correcta es **`i < argc`, sin excepción**. + +## 4.2 Ahora sí: no todos los argumentos son iguales + +En `./programa test.txt --alguna-opcion --dato=2` hay **tres clases** de argumentos. Distinguirlos es +el corazón del parseo. + +| Clase | Ejemplo | Pista para reconocerlo | +|---|---|---| +| **Posicional** | `test.txt` | **No** empieza con `-`. Su **posición** importa (es el "primer dato", el "segundo dato"...). | +| **Opción (flag)** | `--alguna-opcion` | Empieza con `-` o `--` y **no** lleva valor. Activa/desactiva algo. | +| **Opción con valor** | `--dato=2` | Es una opción **y** trae su valor (con `=` o en el argumento siguiente). | + +**Analogía de pedido por delivery.** + +- `pedido.txt` → **qué** hay que procesar (posicional). +- `--rapido` → **una opción**: "querés rápido" (sí/no). +- `--cantidad=2` → **opción con valor**: "querés 2". + +## 4.3 La regla del guión + +La pista principal para reconocer una opción es simple: **empieza con `-`**. + +```text +"-" ... no es opción (guión solo: suele significar "leer de stdin") +"-x" ... opción corta +"--" ... no es una opción (separador especial "fin de opciones") +"--x" ... opción larga +"x" ... argumento posicional +``` + +Cuidado con los casos de la tabla: un `"-"` solito **no** es una opción, y un `"--"` solito tampoco +(suele marcar *"de acá en adelante, todo es posicional"*). A esos casos raros les prestamos atención +en la Parte 5. + +> **Trampa N.º 2 — confundir posición con opción.** Los **posicionales** dependen del **orden**: el +> primer nombre de archivo es el de entrada, el segundo el de salida. Las **opciones** suelen poder ir +> en cualquier orden. No mezcles ambas ideas. + +## 4.4 Un esqueleto de clasificación (patrón, no solución) + +Para clasificar a mano, un patrón típico es mirar la forma del argumento: + +```c +for (int i = 1; i < argc; i++) { + if (argv[i][0] == '-' && argv[i][1] == '-') + /* opción larga */; + else if (argv[i][0] == '-') + /* opción corta */; + else + /* posicional */; +} +``` + +Fijate los índices: `argv[i][0]` es el **primer carácter** de la palabra; `argv[i][1]` el **segundo**. +Por eso `argv[i][0] == '-'` pregunta "¿empieza con guión?" y `argv[i][1] == '-'` pregunta "¿y el +siguiente también?" (o sea, `--`). + +> **Ojo con esto.** Este es un **patrón ilustrativo** para entender cómo se mira una cadena por dentro. +> Cuidado con los **casos de borde** (`"-"` y `"--"`): el patrón de arriba los trataría como opciones. +> Un clasificador robusto los contempla aparte. + +## Práctica de la Parte 4 + +### Ejercicio 13.5 — Listar solo los parámetros útiles + +**Conceptos** · `argc`, `argv`, bucle, índices + +**Contexto** · Muchas herramientas listan lo que recibieron, sin el nombre del programa. +Simplificación: un listado de los argumentos pasados. + +**Consigna** · Escribí un programa que muestre **únicamente** los argumentos posteriores al nombre +(desde `argv[1]`), uno por línea, con el formato del escenario de prueba. + +**Entrada** · Los argumentos pasados al ejecutarlo. +**Salida** · El listado pedido, sin incluir `argv[0]`. + +**Escenario de prueba** + +```text +$ ./filtrar -r --debug datos.csv +Parámetros recibidos: +- -r +- --debug +- datos.csv +``` + +**Identificadores sugeridos** · `argc`, `argv`, `i` + +**Restricciones / pistas** +- Recorré desde `i = 1`. +- No imprimas `argv[0]`. + +**Criterios de aceptación** +- [ ] No muestra el nombre del programa. +- [ ] Recorre de `1` a `argc-1`. +- [ ] Formato idéntico al ejemplo. + +### Ejercicio 13.6 — Contador de parámetros extra + +**Conceptos** · `argc`, aritmética simple + +**Contexto** · Los reportes suelen resumir cuántos datos recibieron. Simplificación: contar los +argumentos extra. + +**Consigna** · Escribí un programa que informe **cuántos argumentos extra** (sin contar el nombre) +recibió, con el formato del escenario de prueba. + +**Entrada** · Los argumentos pasados al ejecutarlo. +**Salida** · Una línea con la cantidad. + +**Escenario de prueba** + +```text +$ ./contar a b c d +Recibiste 4 parámetro(s) extra(s). +``` + +```text +$ ./contar +Recibiste 0 parámetro(s) extra(s). +``` + +**Identificadores sugeridos** · `argc`, `extraCount` + +**Restricciones / pistas** +- La cantidad de extra es `argc - 1`. Los corchetes del ejemplo son literales: no hace falta manejar + plural/singular finamente. + +**Criterios de aceptación** +- [ ] Con `a b c d` informa `4`. +- [ ] Sin argumentos informa `0`. +- [ ] El cálculo usa `argc` (no cuenta a mano). + +### Ejercicio 13.7 — Clasificador de argumentos + +**Conceptos** · `argv[i][0]`, comparación de cadenas, clasificación + +**Contexto** · Antes de interpretar opciones, hay que saber qué tipo de argumento es cada uno. +Simplificación: clasificar sin ejecutar ninguna acción. + +**Consigna** · Escribí un programa que recorra desde `argv[1]` y clasifique cada argumento como: + +- `[OPCION LARGA]` si empieza con `--` y tiene algo después; +- `[OPCION CORTA]` si empieza con **un** `-` y tiene algo después; +- `[POSICIONAL]` si no empieza con `-`. + +**Entrada** · Los argumentos pasados al ejecutarlo. +**Salida** · Una línea por argumento con el formato del ejemplo. + +**Escenario de prueba** + +```text +$ ./clasifica -v --verbose archivo.txt -h +-v -> [OPCION CORTA] +--verbose -> [OPCION LARGA] +archivo.txt -> [POSICIONAL] +-h -> [OPCION CORTA] +``` + +**Identificadores sugeridos** · `argc`, `argv`, `i` + +**Restricciones / pistas** +- Distinguí `--` de `-` mirando el **segundo** carácter. +- Contemplá (aunque sea mínimamente) los casos `"-"` y `"--"` para no clasificarlos mal. + +**Criterios de aceptación** +- [ ] Distingue correctamente los tres tipos. +- [ ] No confunde `--` con `-`. +- [ ] Recorre solo los argumentos extra. +- [ ] El formato coincide con el ejemplo. + +--- + +# PARTE 5 — Opciones cortas y largas (`-v` vs `--verbose`) + +## 5.1 Dos estilos, una misma idea + +Las herramientas de consola usan **dos estilos** para las opciones. No compiten: **conviven**. + +| Estilo | Prefijo | Ejemplos | Para qué sirve | +|---|---|---|---| +| **Corta** | `-` (un guión) | `-v`, `-h`, `-r`, `-o` | Rápida de tipear. Casi siempre letra única. | +| **Larga** | `--` (dos guiones) | `--verbose`, `--help`, `--output` | Se lee como palabra. Ideal para no memorizar letras. | + +**Analogía.** Es como abreviaturas vs. palabras completas: `tel.` vs. `teléfono`. Las dos sirven; la +completa deja menos dudas. + +La buena noticia: casi siempre `-v` y `--verbose` **hacen lo mismo**. Es el mismo botón con dos +etiquetas. Tu trabajo es que las dos etiquetas activen el mismo comportamiento. + +## 5.2 Opciones cortas apiladas: `-abc` + +Un detalle muy cómodo de las opciones **cortas**: se pueden **apilar**. Escribir: + +```bash +./programa -abc +``` + +es lo mismo que: + +```bash +./programa -a -b -c +``` + +Son **tres** opciones cortas juntas. Esto **solo** aplica a las cortas (`-`); las largas (`--`) nunca +se apilan así. + +> **Ojo con esto.** "Apilar" no es gratis: si querés soportarlo **a mano**, tenés que recorrer cada +> carácter del argumento. Por suerte, en la Parte 8 veremos que `getopt` **lo hace por vos**. + +## 5.3 El separador `--` (fin de opciones) + +Existe una convención poderosa: un argumento que es **solo** `--` le dice al programa +*"de acá en adelante, todo es posicional, aunque empiece con guión"*. Se usa para archivos con nombres +raros: + +```bash +./programa -- --archivo-que-empieza-con-guiones.txt +``` + +Acá `--archivo-...` es un **archivo**, no una opción. Es un caso avanzado; mencionarlo te evita +sorpresas cuando leas el código de otras herramientas. + +## 5.4 Detectar una opción con seguridad + +Un chequeo básico y seguro (contemplando el caso `"-"`): + +```c +if (argv[i][0] == '-' && argv[i][1] != '\0') { + /* Es una opción: arranca con '-' y no es solo "-" */ +} +``` + +Ese `!= '\0'` pregunta "¿hay algo **después** del guión?". Así un `"-"` solito (que suele significar +*"leer de la entrada estándar"*) **no** se confunde con una opción. + +## Práctica de la Parte 5 + +### Ejercicio 13.8 — Simulador de ayuda + +**Conceptos** · comparación de cadenas (`strcmp`), opciones cortas y largas, terminación del programa + +**Contexto** · Toda herramienta debe poder explicarse sola. `-h` y `--help` son el estándar de facto. +Simplificación: mostrar una ayuda fija y salir. + +**Consigna** · Escribí un programa que, si recibe **`-h` o `--help`** (cualquiera de las dos), muestre +un mensaje de ayuda y **termine**. Si no las recibe, no debe mostrar la ayuda. + +**Entrada** · Argumentos por línea de comandos. +**Salida** · El mensaje de ayuda (con el nombre del programa incluido), solo si corresponde. + +**Escenario de prueba** + +```text +$ ./ayuda -h +Uso: ./ayuda [opciones] +Opciones: + -h, --help Muestra esta ayuda + -v, --verbose Modo detallado +``` + +```text +$ ./ayuda --help +Uso: ./ayuda [opciones] +Opciones: + -h, --help Muestra esta ayuda + -v, --verbose Modo detallado +``` + +**Identificadores sugeridos** · `argc`, `argv`, `i` + +**Restricciones / pistas** +- Tenés que reconocer **las dos** variantes: `-h` **y** `--help`. +- Para comparar cadenas completas usá `strcmp` (no `==`, que compara direcciones). +- Al mostrar ayuda, el programa debe **terminar**: no sigas procesando. +- La ayuda usa `argv[0]` en la línea `Uso: ...`. + +**Criterios de aceptación** +- [ ] Detecta tanto `-h` como `--help`. +- [ ] Son variantes equivalentes (mismo resultado). +- [ ] Tras mostrar la ayuda, el programa **termina**. +- [ ] La línea `Uso:` usa `argv[0]`. + +--- + +# PARTE 6 — Opciones con valor + +## 6.1 El problema + +Muchas opciones necesitan **un valor**: `--max=10`, `--output=resultado.txt`, `-o salida.txt`. El +valor es el dato que la opción lleva consigo. + +Hay **dos formas** de pasarlo, y las dos son válidas: + +| Forma | Ejemplo | En `argv` queda... | +|---|---|---| +| **Pegada con `=`** | `--dato=2` | **un** argumento: `"--dato=2"` | +| **Separada por espacio** | `--dato 2` | **dos** argumentos: `"--dato"` y `"2"` | + +**Analogía.** Es como decir *"una Coca-Cola grande"* vs. *"una Coca-Cola, grande"*. Mismo significado, +forma distinta. + +> **Momento ajá.** Esto es central. Muchos programas aceptan **las dos formas**. Cuando parseás a mano, +> o contemplás ambas, o al menos **documentás** cuál aceptás. + +## 6.2 Forma 1: con `=` (todo en un argumento) + +Acá el valor vive **dentro** del mismo argumento, después del `=`. + +```c +/* ¿el argumento empieza con "--dato="? */ +if (strncmp(argv[i], "--dato=", 7) == 0) { + char *valor = argv[i] + 7; /* lo que viene después del '=' */ + /* 'valor' apunta a "2" */ +} +``` + +Desarmemos el `+ 7`: `"--dato="` mide **7** caracteres (`-`, `-`, `d`, `a`, `t`, `o`, `=`). Sumar 7 al +puntero lo corre justo **hasta el primer carácter del valor**. Eso es **aritmética de punteros**: si +`argv[i]` apunta al inicio de la cadena, `argv[i] + 7` apunta al octavo carácter. + +> **Para guardar.** `strncmp` compara solo los primeros `n` caracteres. Lo usamos para preguntar +> "¿empieza con `--dato=`?" sin importar qué sigue. Después tomamos el resto con `+ 7`. + +## 6.3 Forma 2: separada por espacio (valor en el argumento siguiente) + +Acá el valor está en **el siguiente** elemento de `argv`. + +```c +/* ¿el argumento es exactamente "--dato" y hay un siguiente? */ +if (strcmp(argv[i], "--dato") == 0 && (i + 1) < argc) { + char *valor = argv[i + 1]; /* el valor es el argumento siguiente */ + i++; /* ¡salto el valor para no reprocesarlo! */ + /* usar 'valor' */ +} +``` + +Los dos detalles que importan: + +1. **`(i + 1) < argc`**: comprobar que **existe** un argumento siguiente. Si no, el usuario escribió + `--dato` al final y **falta** el valor. +2. **`i++`**: avanzar un paso extra para que el bucle **no vuelva a procesar** el valor `"2"` como si + fuera un argumento independiente. + +> **Trampa N.º 3 — comerse el argumento siguiente.** Si te olvidás del `i++`, en la próxima vuelta del +> bucle vas a ver `"2"` y lo vas a clasificar como **posicional** (o peor). El valor de una opción +> **no** es un argumento suelto: es parte de la opción. Avanzá un lugar. + +## 6.4 ¿Y si el valor es un número? + +`argv` siempre guarda **texto**. Si la opción representa un número, hay que **convertirlo**: + +- `atoi("42")` → `42` (cómodo, pero no avisa si la cadena **no** era un número). +- `strtol("42", &fin, 10)` → más robusto: te deja **verificar** que toda la cadena se convirtió. +- `strtod` para números con decimales. + +Elegir bien la conversión es parte de la Parte 7 (robustez). + +## 6.5 Detectar que falta el valor + +Si el usuario escribe `--dato` **y no pone** el valor (forma separada), tenés que **detectarlo** y +avisar. Ese chequeo es el `(i + 1) < argc` del ejemplo: si es falso, el valor **falta**. Ahí se muestra +un error claro y, normalmente, se termina con código de error (Parte 7). + +## Práctica de la Parte 6 + +### Ejercicio 13.9 — Opción con valor (`--max=N`) + +**Conceptos** · `strncmp`, `atoi`, aritmética de punteros + +**Contexto** · Muchas herramientas aceptan límites configurables. Simplificación: leer un máximo. + +**Consigna** · Escribí un programa que detecte la opción `--max=N` (forma pegada con `=`). Si está +presente, muestra `Máximo: N`. Si no está, muestra `Máximo: sin definir`. + +**Entrada** · Argumentos por línea de comandos. +**Salida** · La línea del ejemplo. + +**Escenario de prueba** + +```text +$ ./limite --max=10 +Máximo: 10 +``` + +```text +$ ./limite +Máximo: sin definir +``` + +**Identificadores sugeridos** · `argc`, `argv`, `i`, `maxValue` + +**Restricciones / pistas** +- Detectá el prefijo `--max=` con `strncmp`. +- Extraé el valor sumando al puntero la longitud del prefijo. +- Si no aparece la opción, el valor queda "sin definir" (una bandera booleana te ayuda). + +**Criterios de aceptación** +- [ ] Detecta la forma con `=`. +- [ ] Muestra el valor correcto. +- [ ] Sin la opción, muestra `sin definir`. +- [ ] No crashea si el programa se ejecuta sin argumentos. + +### Ejercicio 13.10 — Opción con valor separado (`--max 10`) + +**Conceptos** · `strcmp`, índice siguiente, `i++` + +**Contexto** · Otra convención muy común: opción y valor separados por espacio. Simplificación: el +mismo máximo, ahora en dos argumentos. + +**Consigna** · Igual que 13.9, pero aceptando la **forma separada por espacio**: `--max 10`. + +**Entrada** · Argumentos por línea de comandos. +**Salida** · La misma que 13.9. + +**Escenario de prueba** + +```text +$ ./limite --max 10 +Máximo: 10 +``` + +**Identificadores sugeridos** · `argc`, `argv`, `i`, `maxValue` + +**Restricciones / pistas** +- Compará la cadena completa con `strcmp`. +- Verificá que exista el argumento siguiente (`i + 1 < argc`). +- No olvides el `i++` para saltar el valor. + +**Criterios de aceptación** +- [ ] Acepta la forma separada por espacio. +- [ ] Salta el valor consumido (`i++`). +- [ ] Verifica que el valor exista antes de usarlo. + +### Ejercicio 13.11 — Aceptar las dos formas + +**Conceptos** · combinación de `strncmp` y `strcmp`, control de flujo + +**Contexto** · Las herramientas profesionales aceptan múltiples formas de escribir lo mismo. +Simplificación: unir la forma pegada y la separada. + +**Consigna** · Mejorá 13.9 y 13.10 en **un solo** programa que acepte **tanto `--max=10` como +`--max 10`**. + +**Entrada** · Argumentos por línea de comandos. +**Salida** · Igual que 13.9. + +**Escenario de prueba** + +```text +$ ./limite --max=10 +Máximo: 10 +``` + +```text +$ ./limite --max 10 +Máximo: 10 +``` + +**Identificadores sugeridos** · `argc`, `argv`, `i`, `maxValue`, `haveMax` + +**Restricciones / pistas** +- Podés preguntar primero por el prefijo `--max=` y, si no coincide, por la igualdad exacta + `--max`. +- Evitá duplicar la conversión: una sola vez que tengas el texto del valor, convertilo. + +**Criterios de aceptación** +- [ ] Funciona con `=` y con espacio. +- [ ] No hay comportamiento distinto entre las dos formas (mismo resultado). +- [ ] El código no repite innecesariamente la lógica de conversión. + +### Ejercicio 13.12 — Detectar valor faltante + +**Conceptos** · control de errores, `stderr`, terminación con código + +**Contexto** · Un programa serio no se cuelga ni crashea si al usuario se le olvida el valor. Avisa y +termina. Simplificación: error de argumento incompleto. + +**Consigna** · Partiendo de 13.10, si el usuario escribe `--max` **sin valor** (al final de la línea), +el programa debe mostrar un error **y terminar**. No debe seguir como si nada. + +**Entrada** · Argumentos por línea de comandos. +**Salida** · Mensaje de error y línea de uso. + +**Escenario de prueba** + +```text +$ ./limite --max +Error: la opción --max necesita un valor +Uso: ./limite [--max= | --max ] +``` + +**Identificadores sugeridos** · `argc`, `argv`, `i`, `ok` + +**Restricciones / pistas** +- El caso es `i + 1 >= argc` cuando la opción es exactamente `--max`. +- El mensaje de error va por la **salida de errores** (`stderr`), no por la salida normal. +- Terminá el programa con un código **distinto de 0** para señalar el fallo (Parte 7). + +**Criterios de aceptación** +- [ ] Detecta `--max` sin valor. +- [ ] Muestra un error claro (sin crashear). +- [ ] Incluye una línea de uso. +- [ ] Termina con código distinto de 0. + +--- + +# PARTE 7 — Validar, avisar y salir con estilo + +## 7.1 La regla de oro + +**"Fracasar con claridad es mejor que romperse en silencio."** + +Un buen programa de consola, ante una entrada inválida: + +1. **Avisa** qué pasó, en un mensaje claro; +2. **Muestra** cómo se usa; +3. **Termina** con un código que indique el error. + +## 7.2 Códigos de salida + +El `return` de `main` es lo que el **shell** (y cualquier script) ve como **código de salida**: + +- **`0`** → éxito (es el único que significa "todo bien"). +- **Distinto de 0** → error (`1`, `2`, etc.). + +Por eso, cuando algo falla: `return 1;`. Quien ejecutó tu programa puede **decidir** en función de eso +(un `if` en un script, un `&&` en la terminal, una tarea automatizada). + +> **Momento ajá.** El código de salida **no** es un capricho: es la forma en que un programa le +> **cuenta** a otro si salió bien. Es la versión "de consola" de un booleano. + +## 7.3 Errores por `stderr`, resultados por `stdout` + +Esto es un detalle **profesional** que marca la diferencia: + +- **Resultados normales** → `stdout` (`printf`). +- **Errores, avisos y ayudas** → `stderr` (`fprintf(stderr, ...)`). + +**¿Por qué?** Porque si alguien guarda la salida normal en un archivo: + +```bash +./programa datos.txt > resultado.txt +``` + +los **errores no deben** colarse en `resultado.txt`. `stdout` va al archivo; `stderr` **sigue +apareciendo** en la pantalla, separado. Los datos siguen limpios. + +```c +fprintf(stderr, "Error: no se encontró el archivo '%s'\n", nombre); +``` + +> **Puente al capítulo siguiente.** `stdin`, `stdout` y `stderr` son las **tres tuberías** de todo +> programa. Acá las usamos apenas; las vas a entender a fondo en +> `14-entrada-salida-estandar`. Por ahora, quedate con la regla: **errores a `stderr`, resultados a +> `stdout`**. + +## 7.4 El mensaje de uso (`--help`) + +Toda herramienta que acepta opciones debería tener `-h`/`--help`. Debe ser **conciso, claro** y usar +`argv[0]`. Un buen formato de referencia: + +```text +Uso: ./programa [opciones] [archivo_salida] +Opciones: + -h, --help Muestra esta ayuda + -v, --verbose Activa modo detallado + -o, --output Especifica archivo de salida + --max Máximo de elementos (por defecto: 100) +``` + +La convención de `[]` y `<>` es vieja y muy útil: + +- `<>` → **obligatorio** (``). +- `[]` → **opcional** (`[archivo_salida]`, `[opciones]`). + +## 7.5 Validar argumentos obligatorios + +Si tu programa **necesita** un archivo, comprobá que esté: + +```c +if (argc < 2) { + fprintf(stderr, "Error: falta el archivo de entrada\n"); + fprintf(stderr, "Uso: %s \n", argv[0]); + return 1; +} +``` + +Ese `argc < 2` significa: "solo vino el nombre del programa, no hay ningún dato". + +## Práctica de la Parte 7 + +### Ejercicio 13.13 — Archivo obligatorio + ayuda + +**Conceptos** · `argc`, `strcmp`, `stderr`, códigos de salida, validación + +**Contexto** · Un procesador de archivos real exige un archivo y ofrece ayuda. Simplificación: la +misma estructura, sin procesar nada de verdad. + +**Consigna** · Creá un programa que: + +1. Si recibe `-h` o `--help` → muestre la ayuda y retorne `0`. +2. Si **no** recibe ningún argumento extra → muestre un error por `stderr`, la línea de uso, y + retorne `1`. +3. Si recibe un nombre de archivo (posicional) → muestre `Procesando: ` por `stdout` y + retorne `0`. + +**Entrada** · Argumentos por línea de comandos. +**Salida** · Según el caso, ayuda, error o confirmación. + +**Escenario de prueba** + +```text +$ ./procesar --help +Uso: ./procesar [opciones] +Opciones: + -h, --help Muestra esta ayuda +``` + +```text +$ ./procesar +Error: se requiere un archivo +Uso: ./procesar [opciones] +``` + +```text +$ ./procesar datos.txt +Procesando: datos.txt +``` + +**Identificadores sugeridos** · `argc`, `argv`, `i`, `fileName` + +**Restricciones / pistas** +- La ayuda tiene **prioridad**: detectala **antes** de validar el obligatorio. +- Los **errores** van a `stderr`; la confirmación normal a `stdout`. +- Usá `argv[0]` en los mensajes de uso. + +**Criterios de aceptación** +- [ ] La ayuda se muestra con `-h` y con `--help`, y retorna `0`. +- [ ] Sin argumentos, el error va a `stderr` y retorna `1`. +- [ ] Con archivo, confirma por `stdout` y retorna `0`. +- [ ] Los mensajes usan `argv[0]`. +- [ ] Los códigos de salida son los correctos. + +### Ejercicio 13.14 — Validar que el valor sea numérico + +**Conceptos** · `strtol`/`isdigit`, validación, `stderr` + +**Contexto** · `--max=abc` no tiene sentido: el máximo debe ser un número. El programa debe avisarlo. +Simplificación: validación de un argumento numérico. + +**Consigna** · Ampliá el programa del 13.11: si `--max` recibe un valor que **no** es un entero válido, +mostrá un error y terminá con código de error. Si es válido, mostrá el máximo. + +**Entrada** · Argumentos por línea de comandos. +**Salida** · El máximo o un error. + +**Escenario de prueba** + +```text +$ ./limite --max=abc +Error: '--max' requiere un número entero +``` + +```text +$ ./limite --max=42 +Máximo: 42 +``` + +**Identificadores sugeridos** · `maxText`, `maxValue`, `end` + +**Restricciones / pistas** +- Una forma robusta: `strtol` y comprobar que **consumió toda** la cadena (`*end == '\0'`) y que no + hubo desbordamiento. +- El error va a `stderr` y el programa retorna distinto de 0. +- **No** uses `atoi` solo: no distingue `"42"` de `"42abc"` ni de `"abc"`. + +**Criterios de aceptación** +- [ ] `--max=abc` produce un error claro por `stderr`. +- [ ] `--max=42` muestra `Máximo: 42`. +- [ ] Un valor parcialmente numérico (`--max=42x`) se rechaza. +- [ ] El programa no crashea y retorna el código correcto. + +--- + +# PARTE 8 — Orden y robustez: `getopt` + +## 8.1 Por qué existe + +Parsear a mano es **perfecto para aprender** (y lo que hiciste hasta acá lo prueba). Pero cuando hay +**muchas** opciones, o querés soportar `-abc` apiladas y detectar faltantes de forma consistente, el +código a mano se vuelve repetitivo y frágil. + +Para eso existe **`getopt()`**, una función estándar (POSIX) que hace ese trabajo por vos. + +> **Para entender antes de usar.** **No saltees las Partes 4 a 7.** Dominar el parseo manual es lo que +> te permite entender **qué hace** `getopt` por dentro, y detectar errores cuando algo no cuadra. + +## 8.2 Qué resuelve `getopt` + +- Maneja opciones **cortas** (`-v`, `-h`, `-o valor`). +- Soporta opciones **apiladas** (`-abc` = `-a -b -c`). +- Sabe cuáles opciones **requieren un valor** y cuáles no. +- Avisa de **opciones desconocidas** y de **valores faltantes**. +- Deja los **posicionales juntos** al final, en `optind` (te lo explico en 8.4). + +## 8.3 La forma de usarlo + +```c +#include /* getopt */ +#include + +int main(int argc, char *argv[]) +{ + int opt; + + /* optstring: letras de opciones cortas. + Un ':' DESPUÉS de una letra significa "esa opción requiere un valor". */ + while ((opt = getopt(argc, argv, "hvf:o:")) != -1) { + switch (opt) { + case 'h': /* -h sin valor */ break; + case 'v': /* -v sin valor */ break; + case 'f': /* -f */ break; + case 'o': /* -o */ break; + case '?': /* opción desconocida o valor faltante */ break; + default: break; + } + } + return 0; +} +``` + +Desarmemos el `"hvf:o:"`: + +| Letra en `optstring` | Significa | +|---|---| +| `h` | opción `-h` (sin valor) | +| `v` | opción `-v` (sin valor) | +| `f:` | opción `-f` que **requiere** un valor (el `:` va **después** de la letra) | +| `o:` | opción `-o` que **requiere** un valor | + +El valor de una opción con `:` queda en la variable global **`optarg`** (`char *`). + +> **Momento ajá.** El `:` va **a la derecha** de la letra que necesita argumento. Leer `"f:"` es decir +> "la opción `-f` lleva un valor". Leer `"hf:"` es decir "`-h` sin valor, `-f` con valor". + +## 8.4 Los posicionales después de `getopt` + +Cuando `getopt` termina (devuelve `-1`), todas las opciones ya se procesaron. La variable global +**`optind`** apunta al **primer argumento que no es opción**. De ahí en adelante, todo es posicional: + +```c +while ((opt = getopt(argc, argv, "hv")) != -1) { /* ... */ } + +for (int i = optind; i < argc; i++) { + printf("Posicional: %s\n", argv[i]); +} +``` + +`getopt` **reordena** internamente la línea para dejarte las opciones primero y los posicionales +después. Vos solo leés desde `optind`. + +## 8.5 Errores que detecta + +- **Opción desconocida:** `getopt` devuelve `'?'` (y suele imprimir un aviso por `stderr`). +- **Valor faltante:** si una opción con `:` aparece sin su valor, también se señala con `'?'` (o `:` + según configuración). Lo habitual es tratar `'?'` mostrando tu propio mensaje de uso, más claro para + el lector. + +## 8.6 Disponibilidad + +`getopt` viene de ``, típico de Linux y macOS. En Windows, con MinGW/GCC o entornos POSIX +(como Git Bash / MSYS2), suele estar disponible. Si compilás con MSVC puro, no; ahí este capítulo te +sirve igual porque ya sabés hacerlo a mano. + +## Práctica de la Parte 8 + +### Ejercicio 13.15 — `getopt` básico + +**Conceptos** · `getopt`, `optstring`, `switch`, opciones apiladas + +**Contexto** · Reescribir el parseo con la función estándar. Simplificación: tres opciones cortas. + +**Consigna** · Usando `getopt`, interpretá: + +- `-h` → mostrá la ayuda y terminá; +- `-v` → activá modo detallado (informá `Modo verbose: ON`); +- `-f ` → requiere un archivo (`Archivo: `). + +**Entrada** · Argumentos por línea de comandos. +**Salida** · Según las opciones recibidas. + +**Escenario de prueba** + +```text +$ ./prog -v -f datos.txt +Modo verbose: ON +Archivo: datos.txt +``` + +```text +$ ./prog -vf datos.txt +Modo verbose: ON +Archivo: datos.txt +``` + +**Identificadores sugeridos** · `opt`, `optstring`, `optarg` + +**Restricciones / pistas** +- El `optstring` correcto para esto es `"hvf:"`. +- El segundo escenario prueba que **`-vf` apiladas** funciona sin código extra (lo hace `getopt`). +- Para `-h`, terminá el programa dentro del `case`. + +**Criterios de aceptación** +- [ ] Usa `getopt` con `optstring` correcto. +- [ ] Acepta `-v` y `-f` juntas (`-vf`). +- [ ] `-f` toma su valor de `optarg`. +- [ ] `-h` muestra ayuda y termina. + +### Ejercicio 13.16 — Posicionales con `getopt` + +**Conceptos** · `optind`, posicionales, orden de argumentos + +**Contexto** · Separar las opciones de los datos "de verdad". Simplificación: listar los archivos +sueltos después de las opciones. + +**Consigna** · Ampliá 13.15 para que, **después** de procesar las opciones, muestre todos los +posicionales restantes (los que quedan desde `optind`). + +**Entrada** · Mezcla de opciones y posicionales. +**Salida** · El efecto de las opciones y luego la lista de posicionales. + +**Escenario de prueba** + +```text +$ ./prog -v entrada.txt salida.txt extra.txt +Modo verbose: ON +Posicionales: + entrada.txt + salida.txt + extra.txt +``` + +**Identificadores sugeridos** · `opt`, `optind`, `i` + +**Restricciones / pistas** +- Recorré desde `optind` hasta `argc-1`. +- No mezcles: las opciones se procesan en el `while`; los posicionales, **después**. + +**Criterios de aceptación** +- [ ] Usa `optind` para ubicar el primer posicional. +- [ ] Lista todos los posicionales en orden. +- [ ] Las opciones no se mezclan con la lista. + +--- + +# PARTE 9 — Nivel pro: `getopt_long` + +## 9.1 El salto + +Ahora sí, **nivel pro**: opciones **largas** (`--verbose`, `--help`, `--output=...`, `--max 10`). + +Para eso existe **`getopt_long()`** (también POSIX, header ``). Es **el estándar** que usan +herramientas como `ls`, `grep` y `gcc`. Todo lo que aprendiste de `getopt` sigue valiendo: +`getopt_long` es su hermano mayor, que además entiende las palabras largas. + +## 9.2 Comparación rápida + +| Característica | `getopt` | `getopt_long` | +|---|---|---| +| Opciones cortas (`-v`) | Sí | Sí | +| Opciones largas (`--verbose`) | **No** | **Sí** | +| Acepta `--opcion=valor` | No | **Sí** | +| Acepta `--opcion valor` | No | **Sí** | +| Manejo de `-abc` apiladas | Sí | Sí | + +## 9.3 Las tres piezas + +Para usar `getopt_long` necesitás **tres** cosas: + +1. incluir ``; +2. declarar una **tabla** de opciones largas (`struct option`); +3. llamar a **`getopt_long`** en lugar de `getopt`. + +## 9.4 La tabla de opciones largas + +```c +#include + +static struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, 'v'}, + {"output", required_argument, 0, 'o'}, + {"max", required_argument, 0, 'm'}, + {0, 0, 0, 0} /* terminador obligatorio */ +}; +``` + +Cada fila tiene **cuatro columnas**: + +1. **Nombre largo** (`"help"`) → se reconoce como `--help`. +2. **¿Lleva valor?** → `no_argument` (flag) o `required_argument` (con valor). +3. **¿A dónde apunta el resultado?** → `0` significa "devolvé el valor de la cuarta columna". +4. **Código corto asociado** → `'h'`, `'v'`, `'o'`, `'m'`. Así la opción larga **y** la corta caen en + el **mismo `case`** del `switch`. + +La última fila `{0,0,0,0}` es el **terminador**: le dice a `getopt_long` dónde acaba la tabla. **No la +olvides** (es el bug clásico de esta función). + +> **Momento ajá.** Gracias a la cuarta columna, `--verbose` y `-v` comparten el `case 'v'`. Escribís +> el comportamiento **una sola vez** y las dos formas lo activan. Eso es elegancia, no magia. + +## 9.5 Cómo se llama + +```c +int opt; +int option_index = 0; + +while ((opt = getopt_long(argc, argv, "hvo:m:", + long_options, &option_index)) != -1) { + switch (opt) { + case 'h': /* -h o --help */ break; + case 'v': /* -v o --verbose */ break; + case 'o': /* -o o --output (optarg) */break; + case 'm': /* -m o --max (optarg) */ break; + case '?': /* desconocida / falta valor */ break; + default: break; + } +} +``` + +El primer string (`"hvo:m:"`) sigue siendo el `optstring` de las opciones **cortas**. La tabla es para +las **largas**. Y `getopt_long` entiende, sin costo extra: + +- `--output=archivo.txt` (pegado con `=`); +- `--output archivo.txt` (separado por espacio). + +Para las largas, el valor llega —igual que siempre— en **`optarg`**. + +## 9.6 Errores + +- Opción larga **desconocida** → `'?'`; +- Opción larga que **requiere valor** y no lo tiene → `'?'`; +- Opción corta **desconocida** → `'?'`. + +Como en `getopt`, lo más limpio es manejar `'?'` mostrando **tu** mensaje de uso y retornando el +código de error. + +## Práctica de la Parte 9 + +### Ejercicio 13.17 — (JEFE) Utilidad de consola completa + +**Conceptos** · `getopt_long`, `struct option`, `optarg`, `optind`, `stderr`, códigos de salida, +validación numérica, ayuda. + +**Contexto** · Reunir **todo** el capítulo en una sola herramienta real de línea de comandos. Es una +**simplificación**: no procesa archivos de verdad, pero su interfaz es la de una utilidad profesional. + +**Consigna** · Desarrollá un programa que interprete estas opciones: + +| Corta | Larga | ¿Valor? | Acción | +|---|---|---|---| +| `-h` | `--help` | no | Muestra la ayuda y termina con `0`. | +| `-v` | `--verbose` | no | Activa el modo detallado. | +| `-o` | `--output` | sí | Guarda el archivo de salida indicado. | +| `-m` | `--max` | sí | Guarda el máximo (número entero). | + +Y que además: + +1. Acepte las **dos formas** de valor para las largas (`--output=x` y `--output x`). +2. Trate los argumentos **no-opción** como archivos posicionales (listados al final). +3. Valide que `--max` sea un entero; si no, error por `stderr` y retorno distinto de 0. +4. Ante una opción desconocida, muestre un error por `stderr`, la línea de uso y retorne distinto de 0. +5. Si no recibe ningún posicional **ni** `--output`, avise que no hay nada que hacer (error claro). + +**Entrada** · Argumentos por línea de comandos. +**Salida** · El efecto de las opciones por `stdout`; los errores por `stderr`. + +**Escenario de prueba** + +```text +$ ./util --verbose --max=10 entrada.txt salida.txt +Modo verbose: ON +Máximo: 10 +Posicionales: + entrada.txt + salida.txt +``` + +```text +$ ./util --max +Error: la opción --max necesita un valor +Uso: ./util [opciones] ... +``` + +```text +$ ./util --max=abc +Error: '--max' requiere un número entero +``` + +```text +$ ./util --desconocida +Error: opción desconocida +Uso: ./util [opciones] ... +``` + +**Identificadores sugeridos** · `opt`, `long_options`, `option_index`, `optind`, `verbose`, +`max_value`, `output_name`, `have_output`, `i` + +**Restricciones / pistas** +- Usá `getopt_long` con el `optstring` `"hvo:m:"` (o el que corresponda a tus short options). +- **No** olvides el terminador `{0,0,0,0}` de la tabla. +- El `-h` tiene **prioridad**: mostralo apenas aparece y terminá. +- Validá `--max` con `strtol` comprobando que se consumió **toda** la cadena. +- Errores → `stderr`; resultados → `stdout`. +- Los posicionales salen de `argv[optind..argc-1]`. + +**Criterios de aceptación** +- [ ] Reconoce `-v`/`--verbose`, `-o`/`--output` y `-m`/`--max`. +- [ ] Acepta valor pegado (`--max=10`) y separado (`--max 10`). +- [ ] `--help`/`-h` muestra ayuda y retorna `0`. +- [ ] `--max` sin valor y `--max=abc` producen errores claros por `stderr` con código de retorno != 0. +- [ ] Una opción desconocida produce error por `stderr` y código != 0. +- [ ] Los posicionales se listan después de las opciones (usando `optind`). +- [ ] Los resultados van a `stdout` y los errores a `stderr`. +- [ ] No hay accesos fuera de rango ni bucles infinitos. + +--- + +## Cierre: lo que te llevás + +Si tuvieras que quedarte con **una sola idea**, que sea esta: **tu programa puede recibir sus órdenes +al nacer**, y esas órdenes llegan como una lista de cadenas que se llama `argv`. + +- **`argc` cuenta, `argv` guarda.** `argc` es cuántos argumentos hay (incluido el nombre); `argv` es la + lista, con `argv[0]` = el nombre del programa. +- **El espacio separa; las comillas agrupan.** `"hola mundo"` entre comillas es **un** argumento. +- **Recorré con `i < argc`.** Nunca toques `argv[argc]`: es la trampa N.º 1. +- **Aprendé a clasificar:** posicional (sin `-`), opción corta (`-x`), opción larga (`--xx`), y opción + con valor (`--xx=valor` o `--xx valor`). +- **Ojo con la trampa N.º 3:** cuando el valor viene separado por espacio, avanzá un índice (`i++`) + para no reprocesarlo. +- **Fracasá con claridad:** errores por `stderr`, ayuda con `argv[0]`, y `return` distinto de 0 ante + error. +- **Dominá lo manual primero** (Partes 4–7); recién después **delegate** en `getopt` y `getopt_long`, + que te dan orden, opciones apiladas y soporte de `--largas` con valor. + +Y el puente hacia lo que viene: hasta ahora viste **cómo entra la orden** (por la línea de comandos). +En `14-entrada-salida-estandar` vas a aprender **cómo entra y sale todo lo demás**: las tres tuberías +`stdin`/`stdout`/`stderr`, la lectura por teclado, los búferes y `EOF`. Los argumentos te dijeron qué +hacer; el capítulo siguiente te enseña a **conversar** con el programa mientras corre. diff --git a/book/14-entrada-salida-estandar.md b/book/14-entrada-salida-estandar.md index 4f8730a..54ec771 100644 --- a/book/14-entrada-salida-estandar.md +++ b/book/14-entrada-salida-estandar.md @@ -1,9 +1,9 @@ -# Capítulo 13 — Entrada y salida estándar (sin archivos) +# Capítulo 14 — Entrada y salida estándar (sin archivos) > Capítulo intermedio, pero **escrito para que se entienda de verdad**. Se apoya en variables (`02`), > operadores (`03`), `if` y bucles (`04`), funciones (`05`) y arreglos y cadenas (`06`). > -> Es el **puente** hacia el capítulo de archivos (`14-archivos`). Todo lo que aprendas acá sobre +> Es el **puente** hacia el capítulo de archivos (`15-archivos`). Todo lo que aprendas acá sobre > `stdin`, búferes y `EOF` lo vas a reusar tal cual para leer archivos. La única diferencia será que, > en vez de leer del teclado, vas a leer de un archivo. @@ -30,7 +30,7 @@ del capítulo. | **7** | Robustez | Que tu programa no se rompa nunca. | | **8** | Proyecto final | Construís tu propia consola de comandos. | -Los **ejercicios** se numeran en orden: `13.1`, `13.2`, `13.3`, y así. Cada uno trae su **escenario de +Los **ejercicios** se numeran en orden: `14.1`, `14.2`, `14.3`, y así. Cada uno trae su **escenario de prueba** (qué entra y qué tiene que salir) para que puedas **comprobar** tu solución. > **Regla de oro del libro:** el capítulo explica y muestra **cómo funciona** cada cosa, pero **los @@ -243,7 +243,7 @@ diferencia: ## Práctica de la Parte 1 -### Ejercicio 13.1 — Separar la salida útil de los avisos +### Ejercicio 14.1 — Separar la salida útil de los avisos **Conceptos** · `stdout`, `stderr`, `fprintf`, redirección de salidas @@ -278,14 +278,14 @@ Aviso: 3 registros tenían fecha inválida. - [ ] Al correr con `>` y `2>`, cada mensaje cae en su archivo y ninguno aparece en el otro. - [ ] Usa `fprintf` indicando el flujo (`stdout`/`stderr`) en cada escritura. -### Ejercicio 13.2 — Detective de flujos +### Ejercicio 14.2 — Detective de flujos **Conceptos** · `stdout`, `stderr`, redirección (`>`, `2>`, `2>&1`), tuberías **Contexto** · Antes de creer que entendiste los flujos, hay que **verlos**. Vas a conectar el mismo programa de varias maneras y comparar qué termina dónde. -**Consigna** · Tomá el programa de 13.1 (o escribí uno mínimo que mande una línea a `stdout` y otra a +**Consigna** · Tomá el programa de 14.1 (o escribí uno mínimo que mande una línea a `stdout` y otra a `stderr`) y corrélo de estas formas, anotando para cada una **qué queda en pantalla** y **qué en cada archivo**: @@ -462,7 +462,7 @@ int fgetc(FILE *stream); /* igual que getc, pero siempre es función */ ## Práctica de la Parte 2 -### Ejercicio 13.3 — Contador de caracteres seguro +### Ejercicio 14.3 — Contador de caracteres seguro **Conceptos** · `getchar`, `int` vs `char`, `EOF`, bytes altos (`0xFF`) @@ -496,7 +496,7 @@ Caracteres leídos: 3 - [ ] Termina limpio en `EOF`, sin bucle infinito. - [ ] El resultado coincide con el tamaño real de la entrada. -### Ejercicio 13.4 — ¿Cuántos saltos de línea hay? +### Ejercicio 14.4 — ¿Cuántos saltos de línea hay? **Conceptos** · `getchar`, comparación de bytes, `'\n'`, `EOF` @@ -617,7 +617,7 @@ limpiar los residuos que deja `scanf`. Guardátelo. ## Práctica de la Parte 3 -### Ejercicio 13.5 — Protección contra desbordamiento +### Ejercicio 14.5 — Protección contra desbordamiento **Conceptos** · `fgets` con búfer fijo, truncamiento, detección de líneas largas, descarte del resto @@ -650,7 +650,7 @@ Aviso: la línea era más larga que el búfer (20 bytes) y fue recortada. - [ ] El resto de la línea se descarta; no contamina la lectura siguiente. - [ ] No hay desbordamiento ni comportamiento indefinido. -### Ejercicio 13.6 — Contador de palabras +### Ejercicio 14.6 — Contador de palabras **Conceptos** · `fgets`, recorrido de una cadena, espacios, bucles @@ -783,7 +783,7 @@ limpiar es el bucle de 4.5. ## Práctica de la Parte 4 -### Ejercicio 13.7 — Corrección de búfer tras `scanf` +### Ejercicio 14.7 — Corrección de búfer tras `scanf` **Conceptos** · `scanf`, `fgets`, `\n` pendiente, limpieza del búfer @@ -816,7 +816,7 @@ Nombre: Ana - [ ] `fgets` obtiene el nombre, **no** una línea vacía. - [ ] Muestra número y nombre correctos. -### Ejercicio 13.8 — Cuestionario de datos mixtos +### Ejercicio 14.8 — Cuestionario de datos mixtos **Conceptos** · `scanf`, `getchar`, `fgets`, saltear blancos, orden de lectura, residuos @@ -854,7 +854,7 @@ Nombre: Juan Gómez - [ ] No quedan residuos: la lectura del nombre **no** está "corrida". - [ ] Comprueba los valores de retorno. -### Ejercicio 13.9 — Menú de opciones de longitud fija +### Ejercicio 14.9 — Menú de opciones de longitud fija **Conceptos** · `getchar`, consumir el resto de la línea, bucles, validación @@ -1022,7 +1022,7 @@ int main(void) { ## Práctica de la Parte 5 -### Ejercicio 13.10 — Validador de edad numérica +### Ejercicio 14.10 — Validador de edad numérica **Conceptos** · `scanf`, valor de retorno, fallo de conversión, descarte de entrada inválida @@ -1052,7 +1052,7 @@ Salida: Entrada inválida: se esperaba un número entero. - [ ] Con un número válido, lo muestra correctamente. - [ ] No deja la entrada "sucia" para una lectura posterior. -### Ejercicio 13.11 — Extracción de tokens con formato avanzado +### Ejercicio 14.11 — Extracción de tokens con formato avanzado **Conceptos** · `scanf` con ancho máximo (`%19s`), supresión (`*`), valor de retorno @@ -1082,7 +1082,7 @@ Salida: valor = 42 - [ ] Extrae el número correctamente en ambos escenarios. - [ ] Comprueba el retorno de `scanf` (`== 1`). -### Ejercicio 13.12 — La calculadora de una línea +### Ejercicio 14.12 — La calculadora de una línea **Conceptos** · `scanf` con formato mixto (`%d %c %d`), valor de retorno @@ -1175,13 +1175,13 @@ Reglas prácticas: 2. **Cuando examinés bytes crudos, usá `unsigned char`.** Comparar `0xC3` contra un `char` con signo trae resultados confusos. 3. **Un `char` no alcanza para todo símbolo.** Para eso existen `wchar_t` y `char32_t` (capítulo - `15-tipos-stddef`). + `16-tipos-stddef`). 4. **No adivines la codificación.** Si los acentos salen "raros", casi siempre es que la codificación del texto no coincide con la que espera la terminal. ## Práctica de la Parte 6 -### Ejercicio 13.13 — Filtro de terminadores de línea +### Ejercicio 14.13 — Filtro de terminadores de línea **Conceptos** · `getchar`, `EOF`, `'\n'`, `'\r'`, `'\0'`, distinción de símbolos @@ -1222,7 +1222,7 @@ d - [ ] Detecta `'\0'` como byte real `0x00` (no lo confunde con `EOF`). - [ ] Termina en el primer `EOF`, con la etiqueta `` incluida. -### Ejercicio 13.14 — Radiografía de un acento +### Ejercicio 14.14 — Radiografía de un acento **Conceptos** · `getchar`, bytes altos, UTF-8 multibyte, `unsigned char` @@ -1359,7 +1359,7 @@ entrada es válida. `strtod` funciona igual, pero para `double`. ## Práctica de la Parte 7 -### Ejercicio 13.15 — Bucle robusto ante cierre de terminal +### Ejercicio 14.15 — Bucle robusto ante cierre de terminal **Conceptos** · `getchar`, `EOF`, corte de bucles, fin de entrada por teclado @@ -1394,7 +1394,7 @@ Fin de la entrada (EOF). Chau. - [ ] Con la entrada del ejemplo, informa **11** caracteres y **2** líneas. - [ ] Muestra un mensaje de cierre y termina. -### Ejercicio 13.16 — Validador de número a prueba de balas +### Ejercicio 14.16 — Validador de número a prueba de balas **Conceptos** · `fgets`, `strtol`, validación de rango, detección de basura @@ -1455,7 +1455,7 @@ leer líneas acotadas, interpretar el comando, validar números, manejar residuo ## Ejercicio final -### Ejercicio 13.17 — (JEFE) Simulador de consola de comandos interactiva +### Ejercicio 14.17 — (JEFE) Simulador de consola de comandos interactiva **Conceptos** · `fgets`, `sscanf`, `stdin`/`stdout`/`stderr`, limpieza de búfer, validación numérica, `EOF`, corte de bucles, comandos con formato. @@ -1550,7 +1550,7 @@ tubería con búfer**. Todo lo demás se deduce de ahí. `EOF`. - **Un carácter no siempre es un byte**: una `á` son dos bytes. -Y el puente hacia lo que viene: **para C, un archivo es un flujo más**. En `14-archivos` vas a reusar +Y el puente hacia lo que viene: **para C, un archivo es un flujo más**. En `15-archivos` vas a reusar **exactamente** estas ideas —`fgetc`, `fgets`, `fscanf`, `feof`, los búferes, `EOF`— solo que sobre un flujo abierto con `fopen` en lugar de sobre `stdin`. Si dominaste este capítulo, el de archivos es casi el mismo capítulo con una apertura y un cierre. diff --git a/book/15-archivos.md b/book/15-archivos.md index 646b2cc..e44dd4d 100644 --- a/book/15-archivos.md +++ b/book/15-archivos.md @@ -1,12 +1,12 @@ -# Capítulo 14 — Archivos +# Capítulo 15 — Archivos > Capítulo intermedio/avanzado. Se apoya en lo aprendido sobre arreglos (`06`), punteros (`07`), > `struct` (`08`), memoria dinámica (`09-memoria-dinamica`), entrada/salida estándar -> (`13-entrada-salida-estandar`) y arenas (`12-arenas`). +> (`14-entrada-salida-estandar`) y arenas (`12-arenas`). > Se recomienda leerlo después de tener claros `malloc`, `free`, el uso de punteros, > el manejo de `stdin`/`stdout`/`stderr` y la idea de reservar memoria en bloques (arenas). -## 14.1 ¿Por qué leer y escribir archivos? +## 15.1 ¿Por qué leer y escribir archivos? Hasta ahora, nuestros programas intercambiaban información únicamente a través de la consola (entrada por teclado y salida por pantalla). Esto está bien para ejercicios cortos, pero @@ -17,7 +17,7 @@ archivos compactados. Un archivo nos permite guardar información en disco para usarla más tarde, compartirla entre programas o procesarla por lotes. El desafío es hacerlo de forma **clara, segura y correcta**. -### 14.1.1 Texto vs Binario +### 15.1.1 Texto vs Binario En C, los archivos se dividen en dos grandes categorías: @@ -32,7 +32,7 @@ binario como si fuera texto corrompe su estructura. Por eso, al abrir un archivo usamos modos distintos: `r`, `w`, `a` para texto y `rb`, `wb`, `ab` (más `+` cuando corresponde) para binario. -### 14.1.2 Recursos: abrir y cerrar +### 15.1.2 Recursos: abrir y cerrar Todo archivo abierto consume un recurso del sistema. Por eso, **siempre hay que cerrarlo** cuando terminamos de usarlo. El par básico es: @@ -47,13 +47,13 @@ Un error común es olvidar `fclose()` o no cerrarlo en todos los caminos posible si ocurre un error a mitad del proceso). Más adelante veremos cómo mitigar esto usando distintas estrategias de gestión de memoria/buffers. -## 14.2 Lectura y escritura de archivos de texto +## 15.2 Lectura y escritura de archivos de texto La librería estándar de C (`stdio.h`) ofrece un conjunto de funciones para trabajar con archivos. Antes de usarlas, veamos **cada una**: cómo se llama, qué recibe y qué devuelve. Casi todas giran en torno al tipo `FILE *`, que es el **manejador** (handle) del archivo abierto. -### 14.2.1 Modos de apertura +### 15.2.1 Modos de apertura `fopen` recibe la ruta y un **modo** que decide qué se puede hacer con el archivo: @@ -79,16 +79,16 @@ Agregando una `b` (`"rb"`, `"wb"`, `"ab"`, ...) se abre en **modo binario**. > > Esa es toda la diferencia (y solo importa en Windows; en Linux/macOS no hay traducción). Por eso los > archivos cuyos bytes deben leerse **exactos** —imágenes, audio, o la cabecera ASCII de un PPM -> mezclada con píxeles— se abren con `"b"`. Ojo: el `\n` de la cabecera PPM del ejemplo (14.5) es un +> mezclada con píxeles— se abren con `"b"`. Ojo: el `\n` de la cabecera PPM del ejemplo (15.5) es un > **byte real del archivo** (`0x0A`), no una traducción del modo texto. -### 14.2.2 Referencia: funciones de texto +### 15.2.2 Referencia: funciones de texto #### `fopen` — abrir un archivo ```c FILE *fopen(const char *path, const char *mode); ``` -- **Recibe:** `path`, la ruta del archivo; `mode`, el modo de apertura (ver 14.2.1). +- **Recibe:** `path`, la ruta del archivo; `mode`, el modo de apertura (ver 15.2.1). - **Devuelve:** un `FILE *` (el manejador del archivo abierto) o `NULL` si no se pudo abrir. - **Ojo:** hay que verificar siempre `NULL` antes de usar el manejador. @@ -191,7 +191,7 @@ Y leído paso a paso: > Fijate en el gráfico: el cursor termina en la posición 9, **justo antes del `\n`**. `fscanf` consume > todos los dígitos de cada número, pero **nunca** el `\n` que sigue al último: ese separador queda > **sin leer**. Si tu programa necesita leer lo que viene inmediatamente después, tenés que "comerte" -> ese separador con `fgetc()`. Es exactamente lo que hace el ejemplo de la sección 14.5.2 antes de leer +> ese separador con `fgetc()`. Es exactamente lo que hace el ejemplo de la sección 15.5.2 antes de leer > los píxeles. #### `feof` — ¿se llegó al final? @@ -230,7 +230,7 @@ int tolower(int c); > `printf`, `scanf`, `malloc`, `free` y `sizeof` ya se vieron en capítulos anteriores; acá nos > concentramos en lo específico de archivos. -### 14.2.3 Ejemplo: leer un archivo carácter por carácter +### 15.2.3 Ejemplo: leer un archivo carácter por carácter Con `fgetc()` leés **un byte por vez**. El valor de retorno es un `int`, no un `char`, para que pueda distinguirse el valor `EOF` (fin de archivo, normalmente `-1`). Este es el patrón clásico @@ -266,7 +266,7 @@ Observá tres detalles que se repiten en **todos** los programas con archivos: 2. El bucle corta cuando `fgetc()` devuelve `EOF`. 3. Se cierran **los dos** archivos, en orden inverso al que se abrieron. -### 14.2.4 Ejemplo: leer línea por línea con `fgets` +### 15.2.4 Ejemplo: leer línea por línea con `fgets` Para trabajar línea a línea, `fgets()` es más cómodo que `fgetc()`. Lee como máximo `n - 1` caracteres, o hasta encontrar un `\n` (que **deja incluido** en el buffer), o hasta el fin del @@ -298,7 +298,7 @@ int main(void) { > buffer, `fgets()` la entrega **cortada** en varios pedazos. Para este capítulo, asumimos líneas > razonables. -### 14.2.5 Ejemplo: escribir con formato (`fprintf`) +### 15.2.5 Ejemplo: escribir con formato (`fprintf`) `fprintf()` es a un archivo lo que `printf()` es a la pantalla: acepta el mismo formato. @@ -325,15 +325,15 @@ int main(void) { Con modo `"w"` el archivo se **trunca** (si ya existía, se borra su contenido). Para **agregar** al final sin borrar lo anterior, se usa el modo `"a"`. -## 14.3 Ejercicios: archivos de texto +## 15.3 Ejercicios: archivos de texto > **Regla fundamental:** ningún ejercicio incluye código de solución. Solo se describe el problema, > entrada/salida, conceptos y criterios de aceptación. Se permiten fragmentos de entrada/salida > esperada como ejemplos. -Estos ejercicios trabajan sobre lo visto en la sección 14.2: `fgetc`, `fgets`, `fprintf` y `feof`. +Estos ejercicios trabajan sobre lo visto en la sección 15.2: `fgetc`, `fgets`, `fprintf` y `feof`. -### Ejercicio 14.1 — Contador de líneas y caracteres (texto, básico) +### Ejercicio 15.1 — Contador de líneas y caracteres (texto, básico) **Conceptos** · `FILE*`, `fopen`, `fclose`, `fgetc`, `feof` @@ -376,7 +376,7 @@ incrementar al encontrar `\n`). Siempre validar que el archivo se abrió correct - [ ] Cierra el archivo con `fclose()` antes de terminar. - [ ] Muestra salida con formato claro. -### Ejercicio 14.2 — Copia de archivo de texto (texto, básico) +### Ejercicio 15.2 — Copia de archivo de texto (texto, básico) **Conceptos** · `FILE*`, `fopen`, `fclose`, `fgetc`, `fputc` @@ -414,7 +414,7 @@ y escribir con `fputc()`. Verificar aperturas y cierres. No modificar el conteni - [ ] Reporta errores de forma clara. - [ ] Cierra ambos archivos. -### Ejercicio 14.3 — Leer líneas y guardar filtradas (texto, intermedio) +### Ejercicio 15.3 — Leer líneas y guardar filtradas (texto, intermedio) **Conceptos** · `fgets`, buffers, cadenas, condicionales @@ -464,7 +464,7 @@ Abrir archivos en modo texto. - [ ] Informa cantidad de líneas copiadas. - [ ] Maneja errores de apertura correctamente. -## 14.4 Lectura y escritura de archivos binarios +## 15.4 Lectura y escritura de archivos binarios Cuando trabajamos con datos estructurados o con formatos como imágenes, necesitamos leer/escribir **bytes exactos**, sin que el sistema modifique nada (ni convierta finales de línea). Para eso @@ -473,10 +473,10 @@ usamos `fread()` y `fwrite()`, junto con funciones para ubicarse dentro del arch > **Antes de leer, mirá.** Un archivo binario no se puede abrir con un editor de texto: vas a ver > basura, y encima el editor puede corromperlo. Para *ver* los bytes sin instalar nada, usá > **ImHex Web** (): un editor hexadecimal que corre entero en el -> navegador. Abrí ahí el BMP de la sección 14.8.6 o el PPM de la 14.5.1, ubicá los offsets y +> navegador. Abrí ahí el BMP de la sección 15.8.6 o el PPM de la 15.5.1, ubicá los offsets y > contrastá campo por campo lo que muestra la herramienta con lo que escribe tu programa. -### 14.4.1 Referencia: funciones binarias +### 15.4.1 Referencia: funciones binarias #### `fread` — leer un bloque ```c @@ -531,7 +531,7 @@ dependiendo del formato y de la plataforma. En este capítulo nos enfocamos en * la estructura byte a byte del archivo** y leerlo correctamente según su especificación, sin asumir conversiones mágicas. -### 14.4.2 Ejemplo: averiguar el tamaño de un archivo +### 15.4.2 Ejemplo: averiguar el tamaño de un archivo Antes de leer un archivo binario completo conviene saber **cuántos bytes tiene**. El truco es moverse al final con `fseek()`, preguntar la posición con `ftell()` y volver al principio con @@ -561,7 +561,7 @@ int main(void) { > `long` alcanza para archivos de tamaño normal. Para archivos gigantes existen `fseeko()` y > `ftello()` (con `off_t`), pero no hacen falta en este capítulo. -### 14.4.3 Ejemplo: leer el archivo completo a memoria +### 15.4.3 Ejemplo: leer el archivo completo a memoria El patrón es: medir el tamaño, reservar un bloque con `malloc()`, leer todo de una sola vez con `fread()` y **siempre** liberar al terminar: @@ -600,7 +600,7 @@ int main(void) { > `fread()` devuelve **cuántos elementos** leyó realmente. Puede leer menos de lo pedido si el > archivo se acorta o si ocurre un error, así que conviene comparar con lo esperado. -### 14.4.4 Ejemplo: leer una cabecera fija a una `struct` +### 15.4.4 Ejemplo: leer una cabecera fija a una `struct` Los archivos binarios suelen empezar con una cabecera de tamaño fijo. Se puede leer de una sola vez dentro de una `struct` que tenga **exactamente** el mismo diseño de bytes: @@ -641,14 +641,14 @@ int main(void) { > `struct` en memoria puede **no coincidir** con el del archivo. En esos casos conviene leer los > campos de a uno con `fread()` a variables sueltas, en lugar de a una `struct` entera. -## 14.5 Ejemplo guiado: otro formato binario — PPM (Netpbm) +## 15.5 Ejemplo guiado: otro formato binario — PPM (Netpbm) Antes del desafío de BMP, practiquemos con un formato binario **más simple todavía**: el **PPM** (Portable PixMap), uno de los formatos **Netpbm**. Su virtud didáctica es que su cabecera es **texto plano** y sus datos son una **matriz de colores sin compresión ni padding**, así que separa muy bien "leer la cabecera" de "leer los píxeles". -### 14.5.1 Descripción del formato PPM (variante P6) +### 15.5.1 Descripción del formato PPM (variante P6) Un archivo `.ppm` binario (variante **P6**) tiene esta estructura: @@ -676,7 +676,7 @@ Algunas diferencias con BMP que conviene notar: | Orden de las filas | Arriba → abajo | Abajo → arriba | | Relleno por fila (padding) | No tiene | Múltiplo de 4 bytes | -### 14.5.2 Cómo leerlo +### 15.5.2 Cómo leerlo El siguiente programa lee la cabecera con `fscanf()`/`fgetc()` (son texto) y luego los píxeles con `fread()` (son binario). El truco clave está en el **paso 3**: después de `maxval` hay @@ -703,7 +703,7 @@ int main(void) { } /* 2. ancho, alto y valor máximo (son números en ASCII). - * Detalle de fscanf: ver la sección 14.2.2. */ + * Detalle de fscanf: ver la sección 15.2.2. */ int width, height, maxval; if (fscanf(fp, "%d %d %d", &width, &height, &maxval) != 3) { fprintf(stderr, "cabecera PPM inválida\n"); @@ -750,14 +750,14 @@ int main(void) { > mismas que necesitarás para BMP: leer y validar la firma, extraer dimensiones, consumir el > separador exacto entre cabecera y datos, y mapear `(x, y)` a un índice del buffer. La diferencia > es que BMP agrega **orden invertido de filas** y **relleno (padding) por fila**, que veremos en la -> sección 14.8. +> sección 15.8. -## 14.6 Gestión de memoria: `malloc/free` vs Arenas +## 15.6 Gestión de memoria: `malloc/free` vs Arenas Al leer archivos, especialmente archivos binarios de tamaño variable o con estructuras anidadas, necesitamos almacenar lo leído en memoria. Tenemos dos enfoques principales: -### 14.6.1 Con `malloc` y `free` +### 15.6.1 Con `malloc` y `free` - Reservamos exactamente lo que necesitamos (`malloc` del tamaño leído). - Liberamos cada bloque cuando ya no lo usamos (`free`). @@ -765,7 +765,7 @@ necesitamos almacenar lo leído en memoria. Tenemos dos enfoques principales: - **Desventaja:** si leemos muchos bloques o estructuras pequeñas, debemos rastrear cada `free`. También complica liberar todo ante un error a mitad del proceso. -### 14.6.2 Con arenas (bump allocator) +### 15.6.2 Con arenas (bump allocator) Recordando el concepto del Capítulo 12: reservamos **un bloque grande** una sola vez, avanzamos un cursor para repartir memoria y **liberamos todo junto** (reiniciamos cursor o @@ -783,7 +783,7 @@ Aplicado a lectura de archivos: a **leer archivos correctamente** y que puedas implementar la lectura **usando malloc/free** y también **usando arenas**, entendiendo cuándo conviene cada uno. -## 14.7 Errores y robustez +## 15.7 Errores y robustez Al trabajar con archivos, hay que considerar fallas posibles: @@ -801,13 +801,13 @@ Al trabajar con archivos, hay que considerar fallas posibles: 4. Cerrar el archivo en todos los caminos (incluso ante errores). Esto puede hacerse con cuidado o estructurando el código para asegurar el cierre. -## 14.8 El formato BMP: documentación de referencia +## 15.8 El formato BMP: documentación de referencia Antes del desafío final, necesitamos entender el formato que vamos a interpretar: **BMP** (Windows Bitmap). Lo elegimos porque es uno de los formatos de imagen **más simples** de leer: guarda los píxeles **sin compresión** y documenta su estructura con cabeceras bien definidas. -### 14.8.1 Estructura general de un BMP +### 15.8.1 Estructura general de un BMP Un archivo `.bmp` se compone de estas partes, en este orden: @@ -816,7 +816,7 @@ Un archivo `.bmp` se compone de estas partes, en este orden: 3. **Paleta de colores** (opcional): solo para profundidades de 1, 4 u 8 bits. 4. **Datos de píxeles** (pixel data): la matriz de píxeles. -### 14.8.2 BITMAPFILEHEADER (14 bytes) +### 15.8.2 BITMAPFILEHEADER (14 bytes) | Offset | Tamaño | Campo | Descripción | |---|---|---|---| @@ -826,7 +826,7 @@ Un archivo `.bmp` se compone de estas partes, en este orden: | 8 | 2 | `bfReserved2` | Reservado, debe ser 0. | | 10 | 4 | `bfOffBits` | Offset en bytes donde comienzan los datos de píxeles. | -### 14.8.3 BITMAPINFOHEADER (40 bytes, versión más común) +### 15.8.3 BITMAPINFOHEADER (40 bytes, versión más común) | Offset | Tamaño | Campo | Descripción | |---|---|---|---| @@ -845,7 +845,7 @@ Un archivo `.bmp` se compone de estas partes, en este orden: **Resumen:** desde el offset 14 hasta el 53 (40 bytes) está toda la información que necesitamos para saber cómo leer los píxeles. -### 14.8.4 Datos de píxeles +### 15.8.4 Datos de píxeles Los píxeles comienzan en el offset indicado por `bfOffBits` (campo del archivo, offset 10). @@ -868,17 +868,17 @@ bytesPorFila = ((biWidth * 3 + 3) / 4) * 4 Las filas reales se leen desde el final hacia el principio si la imagen es bottom-up. -### 14.8.5 Paleta (solo para 1, 4 u 8 bpp) +### 15.8.5 Paleta (solo para 1, 4 u 8 bpp) Para 8 bits por píxel, cada píxel es 1 byte que es un **índice** a una tabla de 256 colores (paleta). Cada entrada de la paleta ocupa 4 bytes: `B`, `G`, `R`, reservado (0). La paleta comienza inmediatamente después de la cabecera de bits (offset `14 + biSize`). -### 14.8.6 Anatomía de un BMP real: ejemplo byte a byte +### 15.8.6 Anatomía de un BMP real: ejemplo byte a byte Las tablas de arriba describen el formato en abstracto. Ahora veamos un **BMP real**, chiquito a propósito (5×3 píxeles, 102 bytes), para seguir cada campo sobre los bytes mismos. Es el -mismo archivo que usamos más adelante para verificar el Ejercicio 14.8. +mismo archivo que usamos más adelante para verificar el Ejercicio 15.8. Render de ejemplo-bmp-5x3.bmp: grilla de 5×3 píxeles con los bordes dibujados en negro coordenada (2, 1) —columna 2, fila 1— y es **verde**: `RGB(34, 177, 76)`. Es una forma > rapidísima de saber si tu programa leyó bien las cabeceras y el padding. -### 14.8.7 Cómo leer un BMP (flujo sugerido) +### 15.8.7 Cómo leer un BMP (flujo sugerido) 1. Abrir el archivo en binario (`rb`). 2. Leer los 14 bytes del **BITMAPFILEHEADER**. @@ -974,7 +974,7 @@ buffer copiá la fila `i` del archivo a la fila `height - 1 - i` de la imagen. > el byte menos significativo va primero. Leerlos con `fread` en un `uint32_t` funciona bien en > máquinas little-endian (x86/x64). -## 14.9 Mostrar la imagen en pantalla (opcional): SDL2 +## 15.9 Mostrar la imagen en pantalla (opcional): SDL2 Hasta acá todo el trabajo fue **leer e interpretar** bytes. Un objetivo opcional y muy motivador es **mostrar la imagen** en una ventana. C no trae esto en su biblioteca estándar: dibujar ventanas es @@ -984,7 +984,7 @@ para empezar es **SDL2**. > Esta sección es **opcional** y va más allá del objetivo central del capítulo (leer archivos). > SDL2 es una **dependencia externa**; si tu entorno no la tiene, podés saltearla sin problema. -### 14.9.1 La idea +### 15.9.1 La idea Ya tenés los píxeles en memoria, con 3 bytes por píxel. SDL2 ofrece una función que **envuelve un buffer de píxeles** en una superficie dibujable, sin copiar los datos: @@ -1013,7 +1013,7 @@ Si en cambio guardaste los píxeles en el orden `B, G, R` tal cual vienen en BMP **intercambian** entre rojo y azul. Este es el error más común: colores correctos pero con rojo y azul cambiados. -### 14.9.2 Programa mínimo de visualización +### 15.9.2 Programa mínimo de visualización Este programa asume que ya cargaste la imagen en `pixels` (un buffer `width * height * 3` en orden `R, G, B`, con la fila `0` **arriba**). Muestra la ventana hasta que la cierres o presiones una @@ -1060,14 +1060,14 @@ int main(void) { } ``` -### 14.9.3 El detalle de las filas: BMP es bottom-up +### 15.9.3 El detalle de las filas: BMP es bottom-up SDL espera la **fila 0 arriba**. BMP, en cambio, guarda la imagen **de abajo hacia arriba**. Si volcás los píxeles tal como los leíste, la imagen va a aparecer **invertida verticalmente**. La solución es copiar cada fila a su posición espejada (`fila destino = height - 1 - fila origen`) al momento de cargar el buffer. -### 14.9.4 Compilar y enlazar +### 15.9.4 Compilar y enlazar SDL2 se enlaza como cualquier biblioteca externa. Con `gcc` y la utilidad `sdl2-config`: @@ -1083,14 +1083,14 @@ distribuciones Linux, el paquete suele llamarse `libsdl2-dev`. > explícito del capítulo. Esta sección es la excepción deliberada y por eso queda marcada como > opcional. -## 14.10 Ejercicios: archivos binarios +## 15.10 Ejercicios: archivos binarios Estos ejercicios ponen a prueba la segunda mitad del capítulo: archivos binarios con `fread` y -`fwrite` (sección 14.4), el formato PPM (14.5), arenas (14.6), errores (14.7) y el formato BMP -(14.8). La regla fundamental de la sección 14.3 sigue en pie: **ningún ejercicio incluye código +`fwrite` (sección 15.4), el formato PPM (15.5), arenas (15.6), errores (15.7) y el formato BMP +(15.8). La regla fundamental de la sección 15.3 sigue en pie: **ningún ejercicio incluye código de solución**. -### Ejercicio 14.4 — Lectura binaria con `fread` (binario, intermedio) +### Ejercicio 15.4 — Lectura binaria con `fread` (binario, intermedio) **Conceptos** · Archivos binarios, `fopen` con `rb`, `fread`, buffers de bytes @@ -1127,21 +1127,21 @@ y leer todo con `fread(buf, 1, size, fp)`. Verificar que `fread` leyó lo espera - [ ] Muestra primeros N bytes en formato legible (hex). - [ ] Libera memoria con `free()` y cierra archivo. -### Ejercicio 14.5 — Lectura binaria usando arena (arenas + binario, intermedio) +### Ejercicio 15.5 — Lectura binaria usando arena (arenas + binario, intermedio) **Conceptos** · Arenas (bump allocator), lectura binaria, bloques grandes -**Consigna** · Repetir el Ejercicio 14.4, pero **reservando toda la memoria necesaria desde una arena** +**Consigna** · Repetir el Ejercicio 15.4, pero **reservando toda la memoria necesaria desde una arena** en lugar de llamar `malloc` directamente para el buffer completo. El programa debe: - Crear una arena con capacidad suficiente para alojar el contenido completo del archivo (o un bloque razonable si se desea leer por chunks; para este ejercicio, leer completo). - Leer el archivo binario completo dentro de esa arena (usando el espacio de la arena). -- Mostrar tamaño y primeros N bytes (igual salida que 14.4). +- Mostrar tamaño y primeros N bytes (igual salida que 15.4). - Liberar **todo junto** al final (reiniciar cursor o liberar el bloque de arena completo). **No** hacer `free` por byte/elemento. **Entrada** · Ruta del archivo binario y `N`. -**Salida** · Igual que Ejercicio 14.4. +**Salida** · Igual que Ejercicio 15.4. **Identificadores sugeridos** · `arena`, `arenaBlock`, `capacity`, `offset` @@ -1158,7 +1158,7 @@ es suficiente para bytes). Leer el archivo completo dentro del espacio asignado - [ ] Libera memoria con **un único `free`** (bloque de arena). No hay frees por partes. - [ ] No hay fugas de memoria ni double-free. -### Ejercicio 14.6 — Escritura binaria estructurada (binario, avanzado) +### Ejercicio 15.6 — Escritura binaria estructurada (binario, avanzado) **Conceptos** · `fwrite`, estructuras, archivos binarios, persistencia @@ -1196,23 +1196,23 @@ Verificar cantidad leída. - [ ] Maneja memoria con `malloc/free` adecuadamente. - [ ] No corrompe datos al escribir/leer. -### Ejercicio 14.7 — Persistencia binaria con arena (binario + arenas, avanzado) +### Ejercicio 15.7 — Persistencia binaria con arena (binario + arenas, avanzado) **Conceptos** · Arenas, `fwrite`/`fread`, buffers desde arena -**Consigna** · Modificar el Ejercicio 14.6 para que **el buffer de lectura** (donde se cargan +**Consigna** · Modificar el Ejercicio 15.6 para que **el buffer de lectura** (donde se cargan los puntos leídos) se reserve **desde una arena**, en lugar de `malloc` directo. También puede reservarse el arreglo original desde arena si se desea. El objetivo es practicar lectura binaria usando espacio de arena. -**Entrada/Salida** · Igual que Ejercicio 14.6. +**Entrada/Salida** · Igual que Ejercicio 15.6. **Criterios de aceptación** - [ ] Buffer de lectura reservado desde arena. - [ ] Lectura con `fread` correcta. - [ ] Verificación OK. - [ ] Liberación con único `free` del bloque de arena (sin frees individuales). -### Ejercicio 14.8 — (JEFE) Leer un archivo BMP pixel a pixel +### Ejercicio 15.8 — (JEFE) Leer un archivo BMP pixel a pixel **Conceptos** · Cabeceras binarias (`fread` de estructuras), little-endian, `fseek`, `ftell`, interpretación de píxeles, `malloc/free` y/o arenas (opcional), validación de formato. @@ -1262,12 +1262,12 @@ Salida: Archivo: imagen.bmp - Podés usar una estructura simple para el conteo de colores (p.ej. guardar los 8 colores más frecuentes o un arreglo de conteos). - Opción extra: repetir la lectura usando una **arena** para los buffers de píxeles. -- **Archivo de prueba ideal**: el BMP de 5×3 de la sección 14.8.6 (102 bytes, tres filas de +- **Archivo de prueba ideal**: el BMP de 5×3 de la sección 15.8.6 (102 bytes, tres filas de color sólido). Es tan chico que verificás todo a mano: `bfOffBits = 54`, `rowSize = 16` (15 bytes de color + 1 de relleno), **3 colores × 15 píxeles** y el píxel central (2, 1) debe dar `R=34 G=177 B=76`. El archivo está en `assets/img/ejemplo-bmp-5x3.bmp`. -- La documentación del formato está en la sección 14.8. Si querés mostrar la imagen, mirá la - sección 14.9 (SDL2, opcional). +- La documentación del formato está en la sección 15.8. Si querés mostrar la imagen, mirá la + sección 15.9 (SDL2, opcional). **Criterios de aceptación** - [ ] Valida la firma `BM` antes de continuar. @@ -1279,4 +1279,4 @@ Salida: Archivo: imagen.bmp - [ ] Maneja archivos inexistentes o corruptos con mensajes de error claros. - [ ] Libera memoria correctamente (con `free` o un solo `free` de la arena). - [ ] (Extra) Implementa la versión con arena y verifica que da el mismo resultado. -- [ ] (Extra) Muestra la imagen en una ventana con SDL2 (ver sección 14.9). +- [ ] (Extra) Muestra la imagen en una ventana con SDL2 (ver sección 15.9). diff --git a/book/16-tipos-stddef.md b/book/16-tipos-stddef.md index c1653ba..49a3147 100644 --- a/book/16-tipos-stddef.md +++ b/book/16-tipos-stddef.md @@ -1,4 +1,4 @@ -# Capítulo 15 — Tipos más usados de C +# Capítulo 16 — Tipos más usados de C > Capítulo avanzado. Se apoya en tipos primitivos (`02`), operadores (`03`), arreglos (`06`), > punteros (`07`) y `struct` (`08`). @@ -9,7 +9,7 @@ --- -## 15.1 ¿Por qué no alcanza con un `int`? +## 16.1 ¿Por qué no alcanza con un `int`? Un `int` resuelve muchísimos casos, pero **no responde de forma portable** tres preguntas: @@ -53,7 +53,7 @@ datos** (valores en bits): --- -## 15.2 El mapa de los tipos +## 16.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): @@ -78,7 +78,7 @@ los que vas a usar todo el tiempo (tamaño típico en un sistema de 64 bits): --- -## 15.3 `size_t` — los tamaños +## 16.3 `size_t` — los tamaños **Cabecera:** `` @@ -101,7 +101,7 @@ uses en un `for` inverso (`for (size_t i = n - 1; i >= 0; i--)` es **infinito**) --- -## 15.4 `wchar_t` — caracteres anchos +## 16.4 `wchar_t` — caracteres anchos **Cabecera:** `` @@ -135,7 +135,7 @@ clave no es "agrandar" el tipo, sino **elegir el correcto**. **Se imprimen con ` --- -## 15.5 `NULL` — el puntero nulo +## 16.5 `NULL` — el puntero nulo **Cabecera:** `` @@ -148,7 +148,7 @@ conceptual. --- -## 15.6 `bool` — verdadero o falso +## 16.6 `bool` — verdadero o falso **Cabecera:** `` (C99; en C23, palabra clave) @@ -161,7 +161,7 @@ usarlo:** si necesitás más de dos estados, un `enum` expresa mejor la intenci --- -## 15.7 Enteros de ancho exacto +## 16.7 Enteros de ancho exacto **Cabecera:** `` @@ -205,7 +205,7 @@ printf("%llu\n", (unsigned long long)big); --- -## 15.8 ¿Tengo que dejar de usar `int`? +## 16.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. @@ -223,7 +223,7 @@ Pasá a un **tipo preciso** solo cuando **exista un motivo**: - **`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 15.7): si lo usás para contar, se desborda enseguida. Y encima la +> hasta 127 (ver la tabla de 16.7): si lo usás para contar, se desborda enseguida. Y encima la > aritmética se hace en `int` igual. ```c @@ -255,7 +255,7 @@ int c = a + b; /* 200: la suma se calcula en int, y al achicar se desborda */ --- -## 15.9 ¿Los tipos chicos ahorran memoria? +## 16.9 ¿Los tipos chicos ahorran memoria? **Depende de dónde.** Guardar **muchos** valores en tipos chicos sí ahorra memoria; una variable suelta, no. @@ -290,7 +290,7 @@ struct Color2 { int r, g, b; }; /* 12 bytes */ --- -## 15.10 Errores comunes +## 16.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`. @@ -301,14 +301,14 @@ struct Color2 { int r, g, b; }; /* 12 bytes */ --- -## 15.11 Ejercicios +## 16.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 15.1 — Radiografía de tipos +### Ejercicio 16.1 — Radiografía de tipos **Conceptos** · `sizeof`, `size_t`, `printf` (`%zu`), tipos de `` y `` @@ -342,7 +342,7 @@ LP64 (Linux/macOS 64-bit) LLP64 (Windows 64-bit) --- -### Ejercicio 15.2 — Sumar con `size_t` +### Ejercicio 16.2 — Sumar con `size_t` **Conceptos** · `size_t`, arreglos, `for`, casos borde @@ -369,7 +369,7 @@ count = 1 ; [-5] -> -5 --- -### Ejercicio 15.3 — El índice que se convierte en gigante +### Ejercicio 16.3 — El índice que se convierte en gigante **Conceptos** · conversiones con/sin signo, `size_t`, comparaciones @@ -395,7 +395,7 @@ explicación: -1 convertido a size_t = SIZE_MAX (18446744073709551615), y SIZE_M --- -### Ejercicio 15.4 — Texto que no entra en un byte +### Ejercicio 16.4 — Texto que no entra en un byte **Conceptos** · `wchar_t`, `L""`, `%lc`, `%ls`, `` @@ -426,7 +426,7 @@ contraste: guardar 'ñ' en un char y usar %c da basura o un byte suelto. --- -### Ejercicio 15.5 — El entero de ancho exacto (pro) +### Ejercicio 16.5 — El entero de ancho exacto (pro) **Conceptos** · ``, tipos de ancho exacto, portabilidad @@ -457,7 +457,7 @@ total = 123456789012345ULL (uint64_t) -> 123456789012345 --- -## 15.12 Cierre +## 16.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, diff --git a/dist/13-argumentos-de-linea-de-comandos.pdf b/dist/13-argumentos-de-linea-de-comandos.pdf new file mode 100644 index 0000000..65d3dc0 Binary files /dev/null and b/dist/13-argumentos-de-linea-de-comandos.pdf differ diff --git a/dist/14-entrada-salida-estandar.pdf b/dist/14-entrada-salida-estandar.pdf index 4a576b8..710731a 100644 Binary files a/dist/14-entrada-salida-estandar.pdf and b/dist/14-entrada-salida-estandar.pdf differ diff --git a/dist/15-archivos.pdf b/dist/15-archivos.pdf index 6487bb9..ebc9f07 100644 Binary files a/dist/15-archivos.pdf and b/dist/15-archivos.pdf differ diff --git a/dist/16-tipos-stddef.pdf b/dist/16-tipos-stddef.pdf index 3782c28..0208671 100644 Binary files a/dist/16-tipos-stddef.pdf and b/dist/16-tipos-stddef.pdf differ