Compare commits

...

18 Commits

Author SHA1 Message Date
horacio 04be717bc7 ppdfviewer, basic, bits and string view 2026-11-14 00:15:02 -03:00
horacio c693541103 Refactor code structure for improved readability and maintainability 2026-11-12 15:27:39 -03:00
horacio 950a48b544 Add new PDF documentation for input devices 2026-11-12 14:42:02 -03:00
horacio 54ac3d140e Add DRM KMS documentation PDF to the distribution 2026-11-12 13:25:10 -03:00
horacio e2195a7ed2 Refactor code structure for improved readability and maintainability 2026-11-12 11:41:45 -03:00
horacio 913efb475b ejercicio de argchivos 2026-11-12 11:27:34 -03:00
horacio 361de2745c files 2026-11-06 10:28:14 -03:00
horacio 82008f51bf practica pipes3-5 2026-11-03 15:00:09 -03:00
horacio f4f56db493 agrego practicas 2026-10-31 11:14:30 -03:00
horacio 392cf493d1 Add 21-makefiles.pdf to the distribution directory 2026-10-24 14:58:40 -03:00
horacio 00441678a4 Add 13-getopt-long.pdf to the distribution 2026-10-24 13:33:59 -03:00
horacio 0177bbbceb Add GDB documentation PDF to distribution 2026-10-21 11:13:22 -03:00
horacio 64e8d471b5 Add 19-fechas.pdf to the distribution folder 2026-10-14 10:35:45 -03:00
horacio 187dd293e3 Add new PDF document for string view tutorial 2026-10-14 10:15:15 -03:00
horacio 9be881e043 Add new PDF document: 17-bits-y-mascaras.pdf 2026-10-14 00:08:47 -03:00
horacio e4a362a8fb Enhance Chapter 12 on Memory Alignment in Arenas
- Expanded section on memory alignment, clarifying the importance of data alignment for CPU efficiency.
- Introduced detailed explanations on type alignment versus data alignment.
- Added tables summarizing type sizes and alignments for clarity.
- Included step-by-step examples demonstrating struct alignment and padding.
- Revised code examples to reflect best practices for memory allocation and alignment.
- Updated PDF pagination to reflect the new content structure.
2026-10-13 23:46:24 -03:00
horacio cfaa0ca470 Add PDF documentation for command line arguments 2026-10-13 22:46:30 -03:00
horacio c609be40f8 Add Chapter 15 on Common C Types to exercise book
This chapter covers the most commonly used types in C, including size_t, wchar_t, NULL, bool, and fixed-width integers. It explains the importance of using these types over int for portability and correctness, along with practical examples and exercises to reinforce understanding.
2026-10-13 22:46:22 -03:00
99 changed files with 22052 additions and 123 deletions
+177 -3
View File
@@ -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). - 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. - 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: 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 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`. Markdown: `book/12-arenas.md` → `dist/12-arenas.pdf`.
@@ -206,3 +262,121 @@ Reglas de diseño (resumen):
> Si se modifica `assets/pdf/print.css`, hay que **regenerar todos** los capítulos para que ningún > Si se modifica `assets/pdf/print.css`, hay que **regenerar todos** los capítulos para que ningún
> PDF quede con un diseño distinto. > PDF quede con un diseño distinto.
---
## Protocolo de escritura de archivos extensos
Cuando el agente (humano o IA) necesita escribir un archivo Markdown de contenido grande
(≥ 600 líneas, como los capítulos del libro), **no usar una única llamada a `write` con todo el contenido**.
Los herramientas de escritura pasan el contenido dentro de un string JSON y los caracteres especiales
(tres backticks ```, `$`, `{`, `}`, `\`, comillas) pueden romper el parseo.
### Enfoque correcto (confirmado en producción)
1. **Escribir con `write` solo los primeros ~500-600 líneas**, hasta la primera sección completa
(hasta un `---` o final de una parte). El archivo se crea correctamente porque el contenido
es manejable para el parser JSON.
2. **Usar `edit` para ir agregando las secciones restantes**. Cada llamada a `edit` reemplaza
una cadena corta (`oldString`) por otra más larga (`newString`). La cadena a buscar es siempre
muy breve (ej: el último texto del archivo actual) y la cadena de reemplazo incluye la nueva sección.
```
oldString: "- [ ] Termina limpiamente al escribir \"exit\"." ← solo la última línea existente
newString: "- [ ] Termina limpiamente al escribir \"exit\".\n\n---\n# PARTE 5 — ..." ← misma + nueva sección
```
3. **Verificar el conteo de líneas** con un bash/PowerShell simple después de cada edit grande,
para confirmar que la sección se incorporó correctamente:
```
Get-Content book\22-file-descriptors.md | Measure-Object -Line
```
### Por qué fallaba el enfoque anterior
Un archivo de capítulo tiene ~1200-1500 líneas con:
- Bloques de código C con backticks triples (```` ```c ````) que el parser JSON confunde
- Strings con comillas escapadas (`\"`) que colisionan con las del string JSON
- Caracteres especiales como `$` en ejemplos de shell o `\n` en strings C
Con chunks de ≤ 600 líneas, el contenido cabe en un string JSON sin romper el parseo.
### Regla práctica
> **Nunca escribir más de 600 líneas de una sola vez con `write`.** Si el contenido total es mayor,
> usar `write` para la primera parte y `edit` sucesivo para el resto.
---
## 11. Corrección de caracteres malformados (mojibake)
Los capítulos en español pueden acumular **codificación doble**: bytes UTF-8 almacenados como si
fuera Latin-1/CP1252, lo que genera mojibake visible como `ú`, `Práctica` o caracteres
`\xef\xbf\xbd` (replacement char) en vez de "práctica". Esto pasa cuando el parser JSON del tool
de escritura corrompe los bytes multibyte.
### Cómo detectar
```python
import os
path = r"C:\Users\horacio\Desarrollo\c-programming\book\TU-CAPITULO.md"
with open(path, "rb") as f:
raw = f.read()
# Si hay bytes no ASCII pero el archivo se decodifica como UTF-8 bien formado,
# puede haber entidades HTML o caracteres dobles codificados.
try:
text = raw.decode("utf-8")
except UnicodeDecodeError:
print("NO es UTF-8 — hay corrupción de encoding")
# Buscar replacement char U+FFFD (EF BF BD)
if b"\xef\xbf\xbd" in raw:
print("Encontrado replacement char — el archivo tiene mojibake")
# Buscar entidades HTML sueltas (ú, é, ñ)
for ent in [b"ú", b"é", b"ñ", b"á", b"ó", b"ú"]:
if ent in raw:
print(f"Entidad HTML detectada: {ent}")
```
### Cómo corregir (reconstruir UTF-8 limpio)
Si el archivo está corrupto, **borrar y reconstruir** desde un script Python que escriba en
UTF-8 nativo. El pattern probado en producción es:
1. **Borrar el archivo corrupto.** No intentes repararlo con `edit` — si ya tiene entidades HTML
mezcladas con bytes dobles, la reparación parcial deja residuos.
2. **Escribir con un script Python que use `encoding='utf-8'`.** El script construye el contenido
como strings de Python (que son Unicode internos) y lo escribe como bytes UTF-8 limpios.
No uses `write` del agente para contenido > 600 líneas.
```python
# rebuild_chapter.py
chapter_file = r"C:\Users\horacio\Desarrollo\c-programming\book\25-drm-kms.md"
with open(chapter_file, "w", encoding="utf-8") as f:
f.write("# Capítulo 25 — DRM/KMS\n") # texto en Unicode → se escribe como UTF-8 limpio
```
3. **Si el contenido es largo**, dividí en chunks de ~250-300 líneas y uso `open(..., 'a')` para
append cada chunk al archivo ya creado.
4. **Verificar** que no queden caracteres corruptos:
```python
raw = open(path, "rb").read()
text = raw.decode("utf-8", errors="replace")
assert "\ufffd" not in text # sin replacement chars
for ent in [b"ú", b"é", b"ñ"]:
assert ent not in raw # sin entidades HTML sueltas
```
### Regla de prevención
> **Nunca pasar texto con acentos/ñ directamente en un string JSON `write`.** Si el contenido del
> capítulo tiene caracteres UTF-8 multibyte, escribirlo como un bloque grande en `write` puede
> corromper la codificación. La forma segura es: script Python que construya el Unicode interno y
> lo escriba con `encoding="utf-8"`.
+288 -13
View File
@@ -231,33 +231,308 @@ que lo vuelven "de producción".
### 12.4.1 Alineación de memoria ### 12.4.1 Alineación de memoria
Los procesadores no leen cualquier dirección igual de rápido, y algunos **exigen** que ciertos tipos #### Por qué a la CPU le importa dónde empieza un dato
empiecen en direcciones múltiplo de su tamaño (`int` a múltiplo de 4, `double` a múltiplo de 8, etc.).
Si repartís tamaños impares (por ejemplo 3 bytes), la próxima asignación puede quedar **desalineada**
y provocar comportamientos raros o lentitud.
La solución es **redondear el tamaño hacia arriba** hasta un múltiplo de la alineación: Los procesadores no leen cualquier dirección igual de rápido, y algunos **exigen** que ciertos tipos
empiecen en direcciones "redondas". Si repartís tamaños impares (3 bytes, 5, 1...) el próximo dato
puede quedar **desalineado** y provocar lentitud o, en arquitecturas estrictas, un *crash*
(comportamiento indefinido).
Pero antes de seguir hay que desarmar una trampa de vocabulario, porque es la raíz de tu duda:
> **"Múltiplo de su tamaño" es un atajo que solo vale para los primitivos.** La regla de verdad es:
> cada tipo exige que su dirección sea **múltiplo de su ALINEACIÓN**, y la alineación **no siempre
> coincide con el tamaño**. Para `int`, `double` y `char` da igual; para un `struct`, casi nunca.
> Eso es exactamente lo que hace que te preguntes si podés mezclar tipos. Podés. Seguí leyendo.
#### La alineación es una propiedad del TIPO, no del dato ni de la arena
Una arena **no tiene tipo**: guarda bytes y no sabe qué vas a poner adentro. La alineación no es una
propiedad del valor ni de la arena: es una propiedad **del tipo** que vos vas a escribir en ese
hueco. El lenguaje ya sabe, para cada tipo, cuánto exige:
| Tipo | Tamaño | Alineación típica (x86-64) |
|------|:------:|:--------------------------:|
| `char` | 1 | 1 |
| `short` | 2 | 2 |
| `int` | 4 | 4 |
| `long`, `double`, puntero | 8 | 8 |
| `struct` | según sus miembros (con relleno) | **= la mayor alineación de sus miembros** |
#### La idea que te salva: UNA alineación para todos
Acá está la respuesta a tu primera pregunta. ¿Podés tener `int`, `char`, `double` y structs complejos
en la **misma** arena, bien alineados? **Sí, y no hace falta calcular nada por tipo.**
La clave: si **cada** bloque arranca en un múltiplo de 8, entonces arranca también en un múltiplo de
4, de 2 y de 1. Un múltiplo de 8 *es* múltiplo de todos los divisores de 8. Así que alineando
**todo** a 8 cubrís a la vez a `char` (1), `int` (4), `double`/puntero (8) y a cualquier struct cuya
alineación sea ≤ 8:
```text
base+0 base+8 base+16 base+24
| | | |
v v v v
[ double ][ int ][ char ][ struct ... ]
^8 ^8 ^8 ^8
todos múltiplos de 8 -> 8, 4, 2 y 1 quedan satisfechos
```
Por eso el ejemplo de `main` en 12.3 puede pedir un `Point` y después 100 `int` del mismo bloque: no
calculás la posición de nadie, **alineás cada reparto al mismo valor** y listo.
> **¿Qué alineación universal uso?** `8` alcanza para `int`, `double` y punteros. Para estar
> cubierto ante *cualquier* tipo (incluido algún SIMD o `long double`), usá
> `ARENA_ALIGN = _Alignof(max_align_t)` (normalmente 16). Es el valor que el estándar considera
> suficiente para todo. Un poquito más de desperdicio, cero dolores de cabeza.
#### Structs: por qué el tamaño NO te dice la alineación
Esta es la parte que confunde. Mirá:
```c ```c
#define ARENA_ALIGN 8 // alineamos a 8 bytes struct Mixed { char tag; int value; }; // sizeof = 8 | _Alignof = 4
struct Pair { double a; double b; }; // sizeof = 16 | _Alignof = 8
struct Quad { int a, b, c, d; }; // sizeof = 16 | _Alignof = 4
```
¿Por qué `Mixed` mide 8 y no 5? Porque el compilador **inserta 3 bytes de relleno** (*padding*)
después de `tag`, para que el `int` caiga alineado a 4. Y mide 8 (múltiplo de 4) para que, en un
arreglo `Mixed items[10]`, el `value` del segundo elemento también quede alineado.
De ahí sale el dato que te ahorra todo el cálculo:
> **La alineación de un tipo SIEMPRE divide a su tamaño.** Es una garantía del estándar de C, y
> existe precisamente para que los arreglos funcionen. Consecuencia directa: si alineás el bloque a
> la alineación del struct, **todos** sus miembros caen alineados solos. Nunca tenés que ubicar
> `tag` ni `value` a mano.
Entonces, tu pregunta "¿cómo calculo, en base al `sizeof`, la posición que debe ocupar un struct
complejo?" tiene una respuesta incómoda y liberadora: **no lo calcules desde el `sizeof`.** El
`sizeof` te dice *cuánto ocupa*, no *dónde tiene que empezar*. Para saber eso, pedile la alineación
al lenguaje:
- `_Alignof(Type)` (C11) te da la alineación en bytes.
- `alignof(Type)` con `<stdalign.h>` (el alias con nombre lindo).
- `_Alignas(16)` en una variable te fuerza *su* alineación.
> **Cuidado con el `sizeof` como fuente de alineación.** `struct Pair` (size 16) pide 8, no 16;
> `struct Quad` (size 16) pide 4, no 16. Si asumís "alineo a `sizeof`" vas a desperdiciar y, peor,
> a equivocarte con structs de tamaño impar (p. ej. size 24 con align 8). La alineación y el tamaño
> son primos hermanos, no gemelos.
#### Ejemplo paso a paso: alineación y tamaño de un struct
Vamos con un caso completo, miembro por miembro:
```c
struct Record {
int a; // 4 bytes
int b; // 4 bytes
double c; // 8 bytes
char d; // 1 byte
};
```
**Paso 1 — la alineación de cada miembro.** En x86/x64, lo habitual es:
| Miembro | Tipo | `_Alignof` |
|:-------:|:----:|:----------:|
| `a` | `int` | 4 |
| `b` | `int` | 4 |
| `c` | `double` | 8 |
| `d` | `char` | 1 |
**Paso 2 — la alineación del struct.** Es la **regla del mayor**: el alineamiento de una estructura
es el mayor alineamiento de entre todos sus miembros.
```text
max(4, 4, 8, 1) = 8
```
Por lo tanto `_Alignof(struct Record)` es **8**.
**Paso 3 — acomodar los miembros (y el relleno).** El compilador ubica cada miembro en el primer
offset que sea múltiplo de *su* alineación:
| Miembro | Alineación | Offset donde cae | Ocupa |
|:-------:|:----------:|:----------------:|:-----:|
| `a` | 4 | 0 | 0..3 |
| `b` | 4 | 4 | 4..7 |
| `c` | 8 | 8 | 8..15 |
| `d` | 1 | 16 | 16 |
| *relleno final* | — | 17 | 17..23 (7 bytes) |
Byte a byte queda así:
```text
offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |17 ... 23
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+----------+
| a (int) | b (int) | c (double) | d | relleno |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+----------+
align ^4 ^4 ^8 ^1
```
- `b` termina en 8 y `c` pide 8: **entra directo, sin relleno interno**.
- `d` ocupa 1 byte (offset 16), pero el struct completo tiene que medir un múltiplo de 8.
**Paso 4 — el `sizeof`.** Contenido real: `4 + 4 + 8 + 1 = 17`. Para que sea múltiplo de 8, se agregan
**7 bytes de relleno al final**:
```text
sizeof(struct Record) = 17 + 7 = 24
```
> **La trampa del relleno final.** El `sizeof` **no** es la suma de los miembros: incluye el relleno.
> Acá `4 + 4 + 8 + 1 = 17`, pero `sizeof` es **24**, no 16. Esos 7 bytes "fantasma" existen para que,
> en un arreglo `struct Record items[7]`, cada elemento arranque alineado a 8. Si te olvidás del
> relleno final, el número no cierra.
#### `_Alignof`: ¿operador, macro o función?
`_Alignof` **no es una función** (no se llama en runtime, no existe un símbolo con ese nombre) y
**tampoco es, en sí mismo, una macro**. Es un **operador unario**, igual que `sizeof`:
- Lleva el guión bajo (`_Alignof`) porque en C11 entró como **palabra clave reservada**: los
identificadores con `_` + mayúscula están reservados al lenguaje, así no choca con código viejo.
- Se evalúa en **tiempo de compilación** y produce un valor de tipo `size_t` (la cantidad de bytes de
alineación).
- Su operando es un **tipo** entre paréntesis (`_Alignof(double)`, `_Alignof(struct Record)`), **no**
una variable.
- El `_` inicial es feo a propósito. Por eso `<stdalign.h>` define el **macro** `alignof`, que se
expande a `_Alignof`. Ahí está tu respuesta fina: `alignof` **es** un macro; `_Alignof` es la
palabra clave. En C23 `alignof` pasó a ser palabra clave por derecho propio (y el macro se jubiló).
Cómo usarlo:
```c
#include <stdio.h>
#include <stdalign.h> // trae el macro `alignof`
typedef struct { int a; int b; double c; char d; } Record;
int main(void) {
printf("sizeof(int) = %zu\n", sizeof(int)); // 4
printf("alignof(double) = %zu\n", alignof(double)); // 8
printf("sizeof(Record) = %zu\n", sizeof(Record)); // 24
printf("alignof(Record) = %zu\n", alignof(Record)); // 8
return 0;
}
```
> **Detalle de `printf`.** `_Alignof` y `sizeof` devuelven `size_t`: imprimilos con `%zu`, no con
> `%d` (si no, el formato no coincide con el tipo y tenés *undefined behavior*).
> **Para la arena.** Este es el número que le pasás a `ARENA_ALIGN`: `_Alignof(max_align_t)` cubre
> cualquier tipo; `_Alignof(Record)` alcanza para los `Record`. Lo importante: la alineación se
> **lee del tipo**, no se adivina desde el `sizeof`.
#### La fórmula, para tontos
El corazón de todo es redondear **hacia arriba** al múltiplo más cercano:
```c
#define ARENA_ALIGN 8 // alineamos a 8 bytes (potencia de 2)
// Redondea n hacia arriba al múltiplo de ARENA_ALIGN más cercano. // Redondea n hacia arriba al múltiplo de ARENA_ALIGN más cercano.
#define ARENA_ALIGN_UP(n) (((n) + (ARENA_ALIGN - 1)) & ~(size_t)(ARENA_ALIGN - 1)) #define ARENA_ALIGN_UP(n) (((n) + (ARENA_ALIGN - 1)) & ~(size_t)(ARENA_ALIGN - 1))
```
La parte `& ~(ARENA_ALIGN - 1)` parece magia negra, pero es solo aritmética de bits. Vamos despacio,
con `A = 8`:
1. **`A - 1 = 7 = 0b0111`.** Es una máscara con los **3 bits de abajo** en 1. Esos 3 bits son justo
los que "sobran" dentro de un bloque de 8 (los números 0..7).
2. **`n + (A - 1)` = `n + 7`.** Sumar 7 "empuja" el número hacia arriba lo suficiente para que,
cualquiera sea su resto dentro del bloque de 8, cruce al próximo límite. Si ya estaba justo en un
límite, el `+7` se queda adentro del mismo bloque y da lo mismo.
3. **`~(A - 1) = ~0b0111 = 0b...11111000`.** El `~` invierte: ahora esos 3 bits bajos están en 0 y el
resto en 1. Hacer `&` con esto **borra el resto**, deja los bits altos intactos.
4. **Resultado:** el mayor múltiplo de 8 que es ≤ `n+7`, o sea el **menor múltiplo de 8 ≥ `n`**.
Tabla, con bits de 4 para que se vea:
| `n` | `n + 7` | `n + 7` en binario | `& ~7` | resultado |
|:---:|:-------:|:------------------:|:------:|:---------:|
| 0 | 7 | `0111` | `0000` | **0** |
| 1 | 8 | `1000` | `1000` | **8** |
| 7 | 14 | `1110` | `1000` | **8** |
| 8 | 15 | `1111` | `1000` | **8** |
| 9 | 16 | `1 0000` | `1 0000` | **16** |
Ahí está toda la fórmula: **sumar `A-1` y borrar los bits bajos con `~`**. Nada más.
> **¿Por qué solo sirve con potencias de 2?** Porque `~(A-1)` solo "borra exactamente los bits del
> resto" cuando `A` es potencia de 2. Para `A = 10` esa máscara no existe y habría que dividir con
> `/` y `%`. Por suerte **todas** las alineaciones estándar son potencias de 2 (1, 2, 4, 8, 16...),
> así que el truco siempre aplica.
#### Aplicala a la posición, no al tamaño (variante robusta)
El capítulo redondea el **tamaño** y da por hecho que `offset` ya estaba alineado. Eso alcanza
mientras el bloque base esté alineado y todos pidan lo mismo. La versión más honesta **redondea el
cursor antes de repartir**, que es lo que de verdad querés: que el hueco *nuevo* empiece alineado.
```c
void *arena_alloc(Arena *a, size_t size) { void *arena_alloc(Arena *a, size_t size) {
size_t aligned = ARENA_ALIGN_UP(size); // reservamos un poco más, para alinear // Subo el cursor del offset al múltiplo de 8 más cercano ...
if (a->offset + aligned > a->capacity) { size_t aligned = ARENA_ALIGN_UP(a->offset);
// ... y recién ahí veo si entra el pedido exacto.
if (aligned + size > a->capacity) {
return NULL; return NULL;
} }
void *ptr = a->buffer + a->offset; void *ptr = a->buffer + aligned; // puntero al hueco ya alineado
a->offset += aligned; a->offset = aligned + size; // el cursor queda donde termina el dato
return ptr; return ptr;
} }
``` ```
La parte `& ~(ARENA_ALIGN - 1)` es un truco de bits: como 8 es potencia de 2, "redondear hacia Diferencia con la versión anterior (la del capítulo, que redondea el tamaño): esta **desperdicia
arriba a múltiplo de 8" se hace con una máscara. Es válido **solo porque la alineación es potencia menos** (usa `size` exacto y solo paga el *padding* necesario antes del bloque) y expresa mejor la
de 2**. idea "alineá la **dirección** donde arranca el dato".
#### Ojo: el bloque base también manda
Todo lo anterior asume que `a->buffer` es múltiplo de 8. Como el puntero que devolvés es
`base + offset`, si `base` está torcido, **todo** queda torcido y alinear el offset no sirve de nada.
- `malloc` **garantiza** la alineación de cualquier tipo, así que un bloque pedido con `malloc` es
seguro.
- En cambio un `unsigned char buffer[4096];` local **puede** quedar alineado a 1, ¡aunque el array
sea grande! Si vas a prestar memoria (ejercicio 12.11), forzá la alineación o, mejor, alineá la
**dirección absoluta**, que se banca hasta una base torcida:
```c
#include <stdint.h> // uintptr_t
void *arena_alloc(Arena *a, size_t size) {
// Alineo la DIRECCIÓN real (base + offset), no solo el offset.
uintptr_t start = (uintptr_t)(a->buffer + a->offset);
uintptr_t aligned = (start + (ARENA_ALIGN - 1)) & ~(uintptr_t)(ARENA_ALIGN - 1);
size_t pad = aligned - start; // los bytes que "sacrifico" para alinear
if (a->offset + pad + size > a->capacity) {
return NULL;
}
a->offset += pad + size;
return (void *)aligned;
}
```
Ese `pad = aligned - start` **es** la respuesta literal a "cómo calculo la posición que debe
ocupar": la posición es `aligned`, y el costo de alinear es `pad`. Simple y sin depender de que
`base` haya venido alineado.
#### Lo que te llevás de alineación
- La alineación es del **tipo**, no del valor ni de la arena.
- Para mezclar tipos **no** calculás posiciones: elegís **una** alineación universal
(`_Alignof(max_align_t)`) y alineás cada reparto. Un múltiplo de 8 ya es múltiplo de 8, 4, 2 y 1.
- La alineación de un struct = la mayor de sus miembros, y **siempre divide a su `sizeof`**. Si el
bloque arranca alineado, los miembros caen alineados solos.
- La fórmula es `(n + A - 1) & ~(A - 1)`, y solo funciona con `A` potencia de 2.
- El bloque **base** también tiene que estar alineado; lo más robusto es alinear la dirección
absoluta, no solo el offset.
### 12.4.2 ¿Y si no entra lo que pedís? ### 12.4.2 ¿Y si no entra lo que pedís?
File diff suppressed because it is too large Load Diff
@@ -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`), > 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`). > 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, > `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. > 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. | | **7** | Robustez | Que tu programa no se rompa nunca. |
| **8** | Proyecto final | Construís tu propia consola de comandos. | | **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. 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 > **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 ## 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 **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. - [ ] 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. - [ ] 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 **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 **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. 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 `stderr`) y corrélo de estas formas, anotando para cada una **qué queda en pantalla** y **qué en cada
archivo**: archivo**:
@@ -462,7 +462,7 @@ int fgetc(FILE *stream); /* igual que getc, pero siempre es función */
## Práctica de la Parte 2 ## 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`) **Conceptos** · `getchar`, `int` vs `char`, `EOF`, bytes altos (`0xFF`)
@@ -496,7 +496,7 @@ Caracteres leídos: 3
- [ ] Termina limpio en `EOF`, sin bucle infinito. - [ ] Termina limpio en `EOF`, sin bucle infinito.
- [ ] El resultado coincide con el tamaño real de la entrada. - [ ] 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` **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 ## 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 **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. - [ ] El resto de la línea se descarta; no contamina la lectura siguiente.
- [ ] No hay desbordamiento ni comportamiento indefinido. - [ ] 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 **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 ## 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 **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. - [ ] `fgets` obtiene el nombre, **no** una línea vacía.
- [ ] Muestra número y nombre correctos. - [ ] 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 **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". - [ ] No quedan residuos: la lectura del nombre **no** está "corrida".
- [ ] Comprueba los valores de retorno. - [ ] 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 **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 ## 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 **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. - [ ] Con un número válido, lo muestra correctamente.
- [ ] No deja la entrada "sucia" para una lectura posterior. - [ ] 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 **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. - [ ] Extrae el número correctamente en ambos escenarios.
- [ ] Comprueba el retorno de `scanf` (`== 1`). - [ ] 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 **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 2. **Cuando examinés bytes crudos, usá `unsigned char`.** Comparar `0xC3` contra un `char` con signo
trae resultados confusos. trae resultados confusos.
3. **Un `char` no alcanza para todo símbolo.** Para eso existen `wchar_t` y `char32_t` (capítulo 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 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. del texto no coincide con la que espera la terminal.
## Práctica de la Parte 6 ## 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 **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`). - [ ] Detecta `'\0'` como byte real `0x00` (no lo confunde con `EOF`).
- [ ] Termina en el primer `EOF`, con la etiqueta `<EOF>` incluida. - [ ] Termina en el primer `EOF`, con la etiqueta `<EOF>` 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` **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 ## 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 **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. - [ ] Con la entrada del ejemplo, informa **11** caracteres y **2** líneas.
- [ ] Muestra un mensaje de cierre y termina. - [ ] 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 **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 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, **Conceptos** · `fgets`, `sscanf`, `stdin`/`stdout`/`stderr`, limpieza de búfer, validación numérica,
`EOF`, corte de bucles, comandos con formato. `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`. `EOF`.
- **Un carácter no siempre es un byte**: una `á` son dos bytes. - **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 **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 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. casi el mismo capítulo con una apertura y un cierre.
+65 -65
View File
@@ -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`), > 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 > `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, > 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). > 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 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 (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 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**. 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: 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) usamos modos distintos: `r`, `w`, `a` para texto y `rb`, `wb`, `ab` (más `+` cuando corresponde)
para binario. 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** Todo archivo abierto consume un recurso del sistema. Por eso, **siempre hay que cerrarlo**
cuando terminamos de usarlo. El par básico es: 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 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. 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. 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 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. 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: `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 > 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 > 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. > **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 #### `fopen` — abrir un archivo
```c ```c
FILE *fopen(const char *path, const char *mode); 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. - **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. - **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 > 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 > 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" > **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. > los píxeles.
#### `feof` — ¿se llegó al final? #### `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 > `printf`, `scanf`, `malloc`, `free` y `sizeof` ya se vieron en capítulos anteriores; acá nos
> concentramos en lo específico de archivos. > 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 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 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`. 2. El bucle corta cuando `fgetc()` devuelve `EOF`.
3. Se cierran **los dos** archivos, en orden inverso al que se abrieron. 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` 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 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 > buffer, `fgets()` la entrega **cortada** en varios pedazos. Para este capítulo, asumimos líneas
> razonables. > 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. `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** 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"`. 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, > **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 > entrada/salida, conceptos y criterios de aceptación. Se permiten fragmentos de entrada/salida
> esperada como ejemplos. > 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` **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. - [ ] Cierra el archivo con `fclose()` antes de terminar.
- [ ] Muestra salida con formato claro. - [ ] 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` **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. - [ ] Reporta errores de forma clara.
- [ ] Cierra ambos archivos. - [ ] 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 **Conceptos** · `fgets`, buffers, cadenas, condicionales
@@ -464,7 +464,7 @@ Abrir archivos en modo texto.
- [ ] Informa cantidad de líneas copiadas. - [ ] Informa cantidad de líneas copiadas.
- [ ] Maneja errores de apertura correctamente. - [ ] 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 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 **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 > **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á > basura, y encima el editor puede corromperlo. Para *ver* los bytes sin instalar nada, usá
> **ImHex Web** (<https://web.imhex.werwolv.net/>): un editor hexadecimal que corre entero en el > **ImHex Web** (<https://web.imhex.werwolv.net/>): 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. > 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 #### `fread` — leer un bloque
```c ```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, la estructura byte a byte del archivo** y leerlo correctamente según su especificación,
sin asumir conversiones mágicas. 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 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 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 > `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. > `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 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: `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 > `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. > 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 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: 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 > `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. > 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** 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 (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 **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". 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: 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 | | Orden de las filas | Arriba → abajo | Abajo → arriba |
| Relleno por fila (padding) | No tiene | Múltiplo de 4 bytes | | 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 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 `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). /* 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; int width, height, maxval;
if (fscanf(fp, "%d %d %d", &width, &height, &maxval) != 3) { if (fscanf(fp, "%d %d %d", &width, &height, &maxval) != 3) {
fprintf(stderr, "cabecera PPM inválida\n"); 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 > 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 > 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 > 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, 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: 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). - Reservamos exactamente lo que necesitamos (`malloc` del tamaño leído).
- Liberamos cada bloque cuando ya no lo usamos (`free`). - 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`. - **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. 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, 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 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** a **leer archivos correctamente** y que puedas implementar la lectura **usando malloc/free**
y también **usando arenas**, entendiendo cuándo conviene cada uno. 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: 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 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. 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** 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: (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. 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: 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. 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. 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 | | 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. | | 8 | 2 | `bfReserved2` | Reservado, debe ser 0. |
| 10 | 4 | `bfOffBits` | Offset en bytes donde comienzan los datos de píxeles. | | 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 | | 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 **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. 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). 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. 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 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). (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`). 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 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 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.
<img width="150" alt="Render de ejemplo-bmp-5x3.bmp: grilla de 5×3 píxeles con los bordes dibujados en negro" <img width="150" alt="Render de ejemplo-bmp-5x3.bmp: grilla de 5×3 píxeles con los bordes dibujados en negro"
style="image-rendering: pixelated;" style="image-rendering: pixelated;"
@@ -958,7 +958,7 @@ buffer copiá la fila `i` del archivo a la fila `height - 1 - i` de la imagen.
> coordenada (2, 1) —columna 2, fila 1— y es **verde**: `RGB(34, 177, 76)`. Es una forma > 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. > 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`). 1. Abrir el archivo en binario (`rb`).
2. Leer los 14 bytes del **BITMAPFILEHEADER**. 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 > el byte menos significativo va primero. Leerlos con `fread` en un `uint32_t` funciona bien en
> máquinas little-endian (x86/x64). > 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 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 **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). > 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. > 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 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: 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 **intercambian** entre rojo y azul. Este es el error más común: colores correctos pero con rojo y
azul cambiados. 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 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 `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 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**. 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`) La solución es copiar cada fila a su posición espejada (`fila destino = height - 1 - fila origen`)
al momento de cargar el buffer. 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`: 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 > explícito del capítulo. Esta sección es la excepción deliberada y por eso queda marcada como
> opcional. > 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 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 `fwrite` (sección 15.4), el formato PPM (15.5), arenas (15.6), errores (15.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 (15.8). La regla fundamental de la sección 15.3 sigue en pie: **ningún ejercicio incluye código
de solución**. 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 **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). - [ ] Muestra primeros N bytes en formato legible (hex).
- [ ] Libera memoria con `free()` y cierra archivo. - [ ] 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 **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: 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 - 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). (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). - 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** - Liberar **todo junto** al final (reiniciar cursor o liberar el bloque de arena completo). **No**
hacer `free` por byte/elemento. hacer `free` por byte/elemento.
**Entrada** · Ruta del archivo binario y `N`. **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` **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. - [ ] Libera memoria con **un único `free`** (bloque de arena). No hay frees por partes.
- [ ] No hay fugas de memoria ni double-free. - [ ] 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 **Conceptos** · `fwrite`, estructuras, archivos binarios, persistencia
@@ -1196,23 +1196,23 @@ Verificar cantidad leída.
- [ ] Maneja memoria con `malloc/free` adecuadamente. - [ ] Maneja memoria con `malloc/free` adecuadamente.
- [ ] No corrompe datos al escribir/leer. - [ ] 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 **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 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 puede reservarse el arreglo original desde arena si se desea. El objetivo es practicar
lectura binaria usando espacio de arena. lectura binaria usando espacio de arena.
**Entrada/Salida** · Igual que Ejercicio 14.6. **Entrada/Salida** · Igual que Ejercicio 15.6.
**Criterios de aceptación** **Criterios de aceptación**
- [ ] Buffer de lectura reservado desde arena. - [ ] Buffer de lectura reservado desde arena.
- [ ] Lectura con `fread` correcta. - [ ] Lectura con `fread` correcta.
- [ ] Verificación OK. - [ ] Verificación OK.
- [ ] Liberación con único `free` del bloque de arena (sin frees individuales). - [ ] 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`, **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. 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 - 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). frecuentes o un arreglo de conteos).
- Opción extra: repetir la lectura usando una **arena** para los buffers de píxeles. - 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 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 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`. `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 - La documentación del formato está en la sección 15.8. Si querés mostrar la imagen, mirá la
sección 14.9 (SDL2, opcional). sección 15.9 (SDL2, opcional).
**Criterios de aceptación** **Criterios de aceptación**
- [ ] Valida la firma `BM` antes de continuar. - [ ] 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. - [ ] Maneja archivos inexistentes o corruptos con mensajes de error claros.
- [ ] Libera memoria correctamente (con `free` o un solo `free` de la arena). - [ ] 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) 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).
@@ -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`), > Capítulo avanzado. Se apoya en tipos primitivos (`02`), operadores (`03`), arreglos (`06`),
> punteros (`07`) y `struct` (`08`). > 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: 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 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): 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:** `<stddef.h>` **Cabecera:** `<stddef.h>`
@@ -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:** `<stddef.h>` **Cabecera:** `<stddef.h>`
@@ -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:** `<stddef.h>` **Cabecera:** `<stddef.h>`
@@ -148,7 +148,7 @@ conceptual.
--- ---
## 15.6 `bool` — verdadero o falso ## 16.6 `bool` — verdadero o falso
**Cabecera:** `<stdbool.h>` (C99; en C23, palabra clave) **Cabecera:** `<stdbool.h>` (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:** `<stdint.h>` **Cabecera:** `<stdint.h>`
@@ -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` **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. 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`. - **`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 > **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. > aritmética se hace en `int` igual.
```c ```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 **Depende de dónde.** Guardar **muchos** valores en tipos chicos sí ahorra memoria; una variable
suelta, no. 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`). - 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`. - 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; > 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, > 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) > nunca el algoritmo) para que compares lo que imprimís. Compilá con un estándar moderno (C11 o C23)
> y avisos activos (`-Wall -Wextra`). > 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 `<stddef.h>` y `<stdint.h>` **Conceptos** · `sizeof`, `size_t`, `printf` (`%zu`), tipos de `<stddef.h>` y `<stdint.h>`
@@ -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 **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 **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`, `<uchar.h>` **Conceptos** · `wchar_t`, `L""`, `%lc`, `%ls`, `<uchar.h>`
@@ -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** · `<stdint.h>`, tipos de ancho exacto, portabilidad **Conceptos** · `<stdint.h>`, 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é 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, tamaño**. `size_t` para tamaños, `uintN_t`/`intN_t` para ancho exacto, `bool` para verdades,
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1405
View File
File diff suppressed because it is too large Load Diff
+3778
View File
File diff suppressed because it is too large Load Diff
+1808
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1077
View File
File diff suppressed because it is too large Load Diff
+1162
View File
File diff suppressed because it is too large Load Diff
+2704
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.
+143
View File
@@ -0,0 +1,143 @@
#include "arena.h"
#include <string.h>
char* arena_strdup(Arena* ref_arena, const char* s){
char* str = (char*) arena_alloc(ref_arena, strlen(s) + 1);
if(str == NULL){
return NULL;
}
strcpy(str, s);
return str;
}
void* arena_chunk_alloc(Arena* ref_arena, Chunk* chunk, size_t s){
size_t aligned = (chunk->offset + (ARENA_MEMORY_ALIGN - 1)) & ~(ARENA_MEMORY_ALIGN -1);
if ((aligned + s <= chunk->size)){
chunk->offset = aligned;
void* start = (void *)(chunk->buffer + chunk->offset);
chunk->offset = aligned + s;
return start;
}
return NULL;
}
Chunk* arena_chunk_create(size_t s){
printf("Creating a new chunk of %zu bytes\n", s);
Chunk* chunk1 = (Chunk*)malloc(sizeof(Chunk));
if(chunk1 == NULL){
return NULL;
}
chunk1->size = s;
chunk1->offset = 0;
chunk1->next = NULL;
chunk1->buffer = (unsigned char *)malloc(s);
if(chunk1->buffer == NULL){
perror("Error creando chunk...\n");
free(chunk1);
chunk1 = NULL;
return NULL;
}
printf("Chunk creado correctamente\n");
return chunk1;
}
void arena_chunk_free(Chunk* chunk){
printf("Liberando %zu bytes de memoria\n", chunk->size);
if(chunk->buffer != NULL){
free(chunk->buffer);
chunk->buffer = NULL;
}
free(chunk);
}
void* arena_alloc(Arena* ref_arena, size_t s){
if(ref_arena == NULL){
return NULL;
}
if(ref_arena->current == NULL){
return NULL;
}
Chunk* current_chunk = ref_arena->current;
void* ptr = arena_chunk_alloc(ref_arena, current_chunk, s);
if(ptr == NULL){
size_t new_size = current_chunk->size * 2;
if(new_size < s){
new_size = s;
}
Chunk *new_chunk = arena_chunk_create(new_size);
if(new_chunk == NULL){
return NULL;
}
current_chunk->next = new_chunk;
new_chunk->next = NULL;
ref_arena->current = new_chunk;
return arena_alloc(ref_arena, s);
}
return ptr;
}
Arena arena_create(size_t s){
Arena new_arena = {
.current = NULL,
.first = NULL
};
Chunk* chunk1 = arena_chunk_create(s);
if(chunk1 == NULL){
perror("Error creando arena\n");
exit(1);
}
new_arena.current = chunk1;
new_arena.first = chunk1;
printf("Arena creada correctamente\n");
return new_arena;
}
void arena_free(Arena* ref_arena){
Chunk* item = ref_arena->first;
while (item != NULL) {
Chunk *next = item->next;
arena_chunk_free(item);
item = next;
}
ref_arena->first = NULL;
ref_arena->current = NULL;
printf("Arena liberada\n");
}
void arena_print(Arena* ref_arena){
printf("ARENA INFO\n");
printf("==========\n");
printf("first: %p\n", ref_arena->first);
printf("next: %p\n", ref_arena->current);
printf("==========\n");
}
+29
View File
@@ -0,0 +1,29 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define ARENA_MEMORY_ALIGN 8
typedef struct Chunk{
size_t size;
size_t offset;
unsigned char *buffer;
struct Chunk *next;
}Chunk;
typedef struct{
Chunk *current;
Chunk *first;
}Arena;
Arena arena_create(size_t);
void* arena_alloc(Arena*, size_t);
void arena_free(Arena*);
void arena_print(Arena*);
Chunk* arena_chunk_create(size_t);
void arena_chunk_free(Chunk*);
void* arena_chunk_alloc(Arena*, Chunk*, size_t);
char* arena_strdup(Arena*, const char*);
+90
View File
@@ -0,0 +1,90 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct{
unsigned char *buffer;
size_t size;
size_t offset;
}Arena;
Arena arena_create(size_t);
void* arena_alloc(Arena *, size_t);
void arena_destroy(Arena *);
int main(void){
Arena arena = arena_create(13);
int *ptrInt1 = (int*)arena_alloc(&arena, sizeof(int));
int *ptrInt2 = (int*)arena_alloc(&arena, sizeof(int));
float *ptrFloat1 = (float*)arena_alloc(&arena, sizeof(float));
char *ptrChar1 = (char*)arena_alloc(&arena, sizeof(char));
int need = sizeof(int)*2 + sizeof(float) + sizeof(char);
printf("sizeof int: %d\n", (int)sizeof(int));
printf("sizeof float: %d\n", (int)sizeof(float));
printf("sizeof char: %d\n", (int)sizeof(char));
printf("Necesitamos: %d\n", need);
if(ptrInt1 != NULL){
*ptrInt1 = 18;
printf("Primer entero: %d\n", *ptrInt1);
}
if(ptrInt2 != NULL){
*ptrInt2 = 27;
printf("Segundo entero: %d\n", *ptrInt2);
}
if(ptrFloat1 != NULL){
*ptrFloat1 = 3.1415f;
printf("Primer flotante: %f\n", *ptrFloat1);
}
if(ptrChar1 != NULL){
*ptrChar1 = 'L';
printf("Primer char: %c\n", *ptrChar1);
}
arena_destroy(&arena);
return 0;
}
Arena arena_create( size_t bytes){
Arena new_arena = {
.buffer = NULL,
.size = bytes,
.offset = 0
};
new_arena.buffer = (unsigned char *)malloc(bytes);
if(new_arena.buffer == NULL){
perror("Error asignando memoria");
exit(1);
}
return new_arena;
}
void* arena_alloc(Arena *ref_arena, size_t bytes){
if(ref_arena->buffer == NULL){
return NULL;
}
if(ref_arena->offset + bytes > ref_arena->size){
perror("Memoria llena\n");
return NULL;
}
void* ptr_ref = ref_arena->buffer + ref_arena->offset;
ref_arena->offset += bytes;
return ptr_ref;
}
void arena_destroy(Arena *ref_arena){
if(ref_arena->buffer != NULL){
free(ref_arena->buffer);
ref_arena->buffer = NULL;
}
}
BIN
View File
Binary file not shown.
+82
View File
@@ -0,0 +1,82 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define FPS 10
typedef struct{
unsigned char *buffer;
size_t size;
size_t offset;
}Arena;
Arena arena_create(size_t);
void* arena_alloc(Arena *, size_t);
void arena_reset(Arena *);
void arena_destroy(Arena *);
int main(void){
srand(time(NULL));
Arena arena = arena_create(100);
for(int i=0; i < FPS; i++){
int* ptrLeft = arena_alloc(&arena, sizeof(int));
if(ptrLeft != NULL){
*ptrLeft = rand()%FPS;
printf("El datos del frame es: %d\n", *ptrLeft);
}
arena_reset(&arena);
}
arena_destroy(&arena);
return 0;
}
Arena arena_create( size_t bytes){
Arena new_arena = {
.buffer = NULL,
.size = bytes,
.offset = 0
};
new_arena.buffer = (unsigned char *)malloc(bytes);
if(new_arena.buffer == NULL){
perror("Error asignando memoria");
exit(1);
}
return new_arena;
}
void* arena_alloc(Arena *ref_arena, size_t bytes){
if(ref_arena->buffer == NULL){
return NULL;
}
if(ref_arena->offset + bytes > ref_arena->size){
perror("Memoria llena\n");
return NULL;
}
void* ptr_ref = ref_arena->buffer + ref_arena->offset;
ref_arena->offset += bytes;
return ptr_ref;
}
void arena_reset(Arena *ref_arena){
ref_arena->offset = 0;
}
void arena_destroy(Arena *ref_arena){
if(ref_arena->buffer != NULL){
free(ref_arena->buffer);
ref_arena->buffer = NULL;
}
}
BIN
View File
Binary file not shown.
+106
View File
@@ -0,0 +1,106 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define FPS 10
#define ARENA_ALIGN 8
#define ALLOC_TYPE(REF, TYPE) (TYPE*)arena_alloc(&REF, sizeof(TYPE))
typedef struct{
unsigned char *buffer;
size_t size;
size_t offset;
}Arena;
Arena arena_create(size_t);
void* arena_alloc(Arena *, size_t);
void arena_reset(Arena *);
void arena_destroy(Arena *);
bool arena_is_aligned(size_t);
int main(void){
Arena arena = arena_create(32);
int *test = ALLOC_TYPE(arena, int);
*test = 5;
arena_alloc(&arena, 3);
arena_alloc(&arena, 1);
arena_alloc(&arena, 5);
arena_alloc(&arena, 9);
arena_alloc(&arena, 8);
arena_alloc(&arena, 1);
arena_destroy(&arena);
return 0;
}
bool arena_is_aligned(size_t pos){
size_t mask = 7;
return (pos & mask) == 0;
}
Arena arena_create( size_t bytes){
Arena new_arena = {
.buffer = NULL,
.size = bytes,
.offset = 0
};
new_arena.buffer = (unsigned char *)malloc(bytes);
if(new_arena.buffer == NULL){
perror("Error asignando memoria");
exit(1);
}
return new_arena;
}
void* arena_alloc(Arena *ref_arena, size_t bytes){
if(ref_arena->buffer == NULL){
return NULL;
}
size_t aligned = ((ref_arena->offset + (size_t)((ARENA_ALIGN - 1))) & ~(ARENA_ALIGN - 1));
if(aligned + bytes > ref_arena->size){
printf("arena_alloc(%d): NULL \n", (int) bytes);
return NULL;
}
void* ptr_ref = ref_arena->buffer + aligned;
printf("arena_alloc(%d) ", (int) bytes);
if(arena_is_aligned((size_t)ptr_ref)){
printf("aligned: true ");
}else{
printf("aligned: false ");
}
printf(" base + %d", (int)aligned);
printf(" offset: %d -> %d \n", (int)ref_arena->offset, (int)aligned);
ref_arena->offset = aligned + bytes;
return ptr_ref;
}
void arena_reset(Arena *ref_arena){
ref_arena->offset = 0;
}
void arena_destroy(Arena *ref_arena){
if(ref_arena->buffer != NULL){
free(ref_arena->buffer);
ref_arena->buffer = NULL;
}
}
BIN
View File
Binary file not shown.
+107
View File
@@ -0,0 +1,107 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#define ARENA_ALIGN 8
typedef struct{
unsigned char *buffer;
size_t size;
size_t offset;
}Arena;
Arena arena_create(size_t);
void* arena_alloc(Arena *, size_t);
void arena_reset(Arena *);
void arena_destroy(Arena *);
bool arena_is_aligned(size_t);
char* arena_strdup(Arena*, const char *);
int main(void){
Arena arena = arena_create(16);
char* s1 = arena_strdup(&arena, "arena");
char* s2 = arena_strdup(&arena, "hola");
char* s3 = arena_strdup(&arena, "x");
arena_destroy(&arena);
return 0;
}
char* arena_strdup(Arena* ref_arena, const char *src){
char* ref_str = (char*) arena_alloc(ref_arena, strlen(src) + 1);
if(ref_str == NULL){
perror("No se ha podido reservar memoria para la cadena.");
return NULL;
}
strcpy(ref_str, src);
return ref_str;
}
bool arena_is_aligned(size_t pos){
size_t mask = 7;
return (pos & mask) == 0;
}
Arena arena_create( size_t bytes){
Arena new_arena = {
.buffer = NULL,
.size = bytes,
.offset = 0
};
new_arena.buffer = (unsigned char *)malloc(bytes);
if(new_arena.buffer == NULL){
perror("Error asignando memoria");
exit(1);
}
return new_arena;
}
void* arena_alloc(Arena *ref_arena, size_t bytes){
if(ref_arena->buffer == NULL){
return NULL;
}
size_t aligned = ((ref_arena->offset + (size_t)((ARENA_ALIGN - 1))) & ~(ARENA_ALIGN - 1));
if(aligned + bytes > ref_arena->size){
printf("arena_alloc(%d): NULL \n", (int) bytes);
return NULL;
}
void* ptr_ref = ref_arena->buffer + aligned;
printf("arena_alloc(%d) ", (int) bytes);
if(arena_is_aligned((size_t)ptr_ref)){
printf("aligned: true ");
}else{
printf("aligned: false ");
}
printf(" base + %d", (int)aligned);
printf(" offset: %d -> %d \n", (int)ref_arena->offset, (int)aligned);
ref_arena->offset = aligned + bytes;
return ptr_ref;
}
void arena_reset(Arena *ref_arena){
ref_arena->offset = 0;
}
void arena_destroy(Arena *ref_arena){
if(ref_arena->buffer != NULL){
free(ref_arena->buffer);
ref_arena->buffer = NULL;
}
}
BIN
View File
Binary file not shown.
+120
View File
@@ -0,0 +1,120 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#define ARENA_ALIGN 8
typedef struct{
unsigned char *buffer;
size_t size;
size_t offset;
}Arena;
Arena arena_create(size_t);
void* arena_alloc(Arena *, size_t);
void arena_reset(Arena *);
void arena_destroy(Arena *);
bool arena_is_aligned(size_t);
size_t arena_checkpoint(Arena*);
void arena_restore(Arena*, size_t);
int main(void){
Arena arena = arena_create(32);
size_t cp0 = arena_checkpoint(&arena);
int* x = (int*)arena_alloc(&arena, 8);
*x = 21;
arena_alloc(&arena, 8);
size_t cp1 = arena_checkpoint(&arena);
int* tmp = (int*)arena_alloc(&arena, 8);
arena_restore(&arena, cp1);
printf("x:%d\n", *x);
arena_alloc(&arena, 16);
arena_restore(&arena, cp0);
arena_alloc(&arena, 32);
arena_destroy(&arena);
return 0;
}
void arena_restore(Arena* ref_arena, size_t chk){
ref_arena->offset = chk;
}
size_t arena_checkpoint(Arena* ref_arena){
return ref_arena->offset;
}
bool arena_is_aligned(size_t pos){
size_t mask = 7;
return (pos & mask) == 0;
}
Arena arena_create( size_t bytes){
Arena new_arena = {
.buffer = NULL,
.size = bytes,
.offset = 0
};
new_arena.buffer = (unsigned char *)malloc(bytes);
if(new_arena.buffer == NULL){
perror("Error asignando memoria");
exit(1);
}
return new_arena;
}
void* arena_alloc(Arena *ref_arena, size_t bytes){
if(ref_arena->buffer == NULL){
return NULL;
}
size_t aligned = ((ref_arena->offset + (size_t)((ARENA_ALIGN - 1))) & ~(ARENA_ALIGN - 1));
if(aligned + bytes > ref_arena->size){
printf("arena_alloc(%d): NULL \n", (int) bytes);
return NULL;
}
void* ptr_ref = ref_arena->buffer + aligned;
printf("arena_alloc(%d) ", (int) bytes);
if(arena_is_aligned((size_t)ptr_ref)){
printf("aligned: true ");
}else{
printf("aligned: false ");
}
printf(" base + %d", (int)aligned);
printf(" offset: %d -> %d \n", (int)ref_arena->offset, (int)aligned);
ref_arena->offset = aligned + bytes;
return ptr_ref;
}
void arena_reset(Arena *ref_arena){
ref_arena->offset = 0;
}
void arena_destroy(Arena *ref_arena){
if(ref_arena->buffer != NULL){
free(ref_arena->buffer);
ref_arena->buffer = NULL;
}
}
BIN
View File
Binary file not shown.
+178
View File
@@ -0,0 +1,178 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define ARENA_MEMORY_ALIGN 8
typedef struct Chunk{
size_t size;
size_t offset;
unsigned char *buffer;
struct Chunk *next;
}Chunk;
typedef struct{
Chunk *current;
Chunk *first;
}Arena;
Arena arena_create(size_t);
void* arena_alloc(Arena*, size_t);
void arena_free(Arena*);
void arena_print(Arena*);
Chunk* arena_chunk_create(size_t);
void arena_chunk_free(Chunk*);
void* arena_chunk_alloc(Arena*, Chunk*, size_t);
int main(void){
Arena arena = arena_create(32);
arena_print(&arena);
arena_alloc(&arena, 100);
arena_print(&arena);
arena_alloc(&arena, 40);
arena_print(&arena);
arena_free(&arena);
arena_print(&arena);
return 0;
}
void* arena_chunk_alloc(Arena* ref_arena, Chunk* chunk, size_t s){
size_t aligned = (chunk->offset + (ARENA_MEMORY_ALIGN - 1)) & ~(ARENA_MEMORY_ALIGN -1);
if ((aligned + s <= chunk->size)){
chunk->offset = aligned;
void* start = (void *)(chunk->buffer + chunk->offset);
chunk->offset = aligned + s;
return start;
}
return NULL;
}
Chunk* arena_chunk_create(size_t s){
printf("Creating a new chunk of %zu bytes\n", s);
Chunk* chunk1 = (Chunk*)malloc(sizeof(Chunk));
if(chunk1 == NULL){
return NULL;
}
chunk1->size = s;
chunk1->offset = 0;
chunk1->next = NULL;
chunk1->buffer = (unsigned char *)malloc(s);
if(chunk1->buffer == NULL){
perror("Error creando chunk...\n");
free(chunk1);
chunk1 = NULL;
return NULL;
}
printf("Chunk creado correctamente\n");
return chunk1;
}
void arena_chunk_free(Chunk* chunk){
printf("Liberando %zu bytes de memoria\n", chunk->size);
if(chunk->buffer != NULL){
free(chunk->buffer);
chunk->buffer = NULL;
}
free(chunk);
}
void* arena_alloc(Arena* ref_arena, size_t s){
if(ref_arena == NULL){
return NULL;
}
if(ref_arena->current == NULL){
return NULL;
}
Chunk* current_chunk = ref_arena->current;
void* ptr = arena_chunk_alloc(ref_arena, current_chunk, s);
if(ptr == NULL){
size_t new_size = current_chunk->size * 2;
if(new_size < s){
new_size = s;
}
Chunk *new_chunk = arena_chunk_create(new_size);
if(new_chunk == NULL){
return NULL;
}
current_chunk->next = new_chunk;
new_chunk->next = NULL;
ref_arena->current = new_chunk;
return arena_alloc(ref_arena, s);
}
return ptr;
}
Arena arena_create(size_t s){
Arena new_arena = {
.current = NULL,
.first = NULL
};
Chunk* chunk1 = arena_chunk_create(s);
if(chunk1 == NULL){
perror("Error creando arena\n");
exit(1);
}
new_arena.current = chunk1;
new_arena.first = chunk1;
printf("Arena creada correctamente\n");
return new_arena;
}
void arena_free(Arena* ref_arena){
Chunk* item = ref_arena->first;
while (item != NULL) {
Chunk *next = item->next;
arena_chunk_free(item);
item = next;
}
ref_arena->first = NULL;
ref_arena->current = NULL;
printf("Arena liberada\n");
}
void arena_print(Arena* ref_arena){
printf("ARENA INFO\n");
printf("==========\n");
printf("first: %p\n", ref_arena->first);
printf("next: %p\n", ref_arena->current);
printf("==========\n");
}
BIN
View File
Binary file not shown.
+86
View File
@@ -0,0 +1,86 @@
#include <stdio.h>
#include "../arena-lib/arena.h"
#include <string.h>
typedef struct Persona{
char* nombre;
int edad;
struct Persona* next;
}Persona;
void print_persona(Persona*);
int main(void){
printf("Alinacion struct Persona: %d\n", (int)_Alignof(Persona));
printf("sizeof struct Persona: %d\n", (int)sizeof(Persona));
Arena arena = arena_create(32);
char continuar = 'S';
Persona* first = NULL;
Persona* last = NULL;
do{
char input_name[50];
printf("Nombre:");
if (fgets(input_name, sizeof(input_name), stdin) != NULL) {
input_name[strcspn(input_name, "\n")] = '\0';
}
int input_edad = 0;
printf("Edad:");
scanf("%d", &input_edad);
printf("\n");
int c;
while ((c = getchar()) != '\n' && c != EOF);
Persona* p = (Persona*)arena_alloc(&arena, sizeof(Persona));
if(p == NULL){
perror("Ha ocurrido un error\n");
exit(1);
}
p->nombre = arena_strdup(&arena, input_name);
p->edad = input_edad;
p->next = NULL;
if(first == NULL){
first = p;
}
if(last != NULL){
last->next = p;
}
last = p;
printf("Cargar mas personas? (S) Continuar, (N): Terminar :");
scanf(" %c", &continuar);
while ((c = getchar()) != '\n' && c != EOF);
}while((continuar == 'S') || (continuar == 's'));
printf("Listado de personas cargadas:\n");
printf("=============================\n");
Persona* current = first;
while(current != NULL){
print_persona(current);
current = current->next;
}
arena_free(&arena);
return 0;
}
void print_persona(Persona* ref_persona) {
printf("%s: tiene %d years\n", ref_persona->nombre, ref_persona->edad);
}
BIN
View File
Binary file not shown.
+102
View File
@@ -0,0 +1,102 @@
#include "../arena-lib/arena.h"
#include <stdio.h>
typedef enum {
BIN_OP_SUMA,
BIN_OP_RESTA,
BIN_OP_MULTIPLY,
BIN_OP_DIVISION
} BinaryOps;
typedef enum {
NODE_NUMBER,
NODE_OPERATION
} NodeKind;
typedef struct Node{
union {
BinaryOps operation;
int number;
}value;
NodeKind type;
struct Node* left;
struct Node* right;
} Node;
Node* create_number(Arena* arena_ref, int n){
Node* node = (Node*)arena_alloc(arena_ref, sizeof(Node));
if(node == NULL){
return NULL;
}
node->type = NODE_NUMBER;
node->value.number = n;
node->left = NULL;
node->right = NULL;
return node;
}
Node* create_operation(Arena* ref_arena, BinaryOps op, Node* l, Node* r){
Node* node = (Node*)arena_alloc(ref_arena, sizeof(Node));
if(node == NULL){
return NULL;
}
node->type = NODE_OPERATION;
node->value.operation = op;
node->left = l;
node->right = r;
return node;
}
int node_compute(Node* node){
if(node->type == NODE_OPERATION){
int r1 = node_compute(node->left);
int r2 = node_compute(node->right);
int result = 0;
switch (node->value.operation) {
case BIN_OP_SUMA:
result = r1 + r2;
break;
case BIN_OP_RESTA:
result = r1 - r2;
break;
case BIN_OP_MULTIPLY:
result = r1 * r2;
break;
case BIN_OP_DIVISION:
result = (r2 != 0) ? r1 / r2 : 0;
break;
default:
result = 0;
break;
}
return result;
}
return node->value.number;
}
int main(void){
Arena arena = arena_create(32);
printf("Memory struct information\n");
printf("=========================\n");
printf("sizeof(Node):%zu\n", sizeof(Node));
printf("align(Node): %zu\n", _Alignof(Node));
//Creamos el arbol para 3 + (4 - 1)
Node* t1 = create_number(&arena, 3);
Node* t21 = create_number(&arena, 4);
Node* t22 = create_number(&arena, 1);
Node* t2 = create_operation(&arena, BIN_OP_RESTA, t21, t22);
Node* root = create_operation(&arena, BIN_OP_SUMA, t1, t2);
printf("Node compute\n");
printf("============\n");
printf("result: %d\n", node_compute(root));
arena_free(&arena);
return 0;
}
BIN
View File
Binary file not shown.
+73
View File
@@ -0,0 +1,73 @@
#include <limits.h>
#include <errno.h>
#include<stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include<getopt.h>
static struct option option_args[] = {
{"help", no_argument, 0, 'h' },
{"verbose", no_argument, 0, 'v' },
{"output", required_argument, 0, 'o' },
{"max", required_argument, 0, 'm' },
{0,0,0,0}
};
void print_usage(char* arg){
fprintf(stdout, "Uso: %s\n", arg);
fprintf(stdout, "[--help]\n");
fprintf(stdout, "[--verbose]\n");
fprintf(stdout, "[--output <archivo>]\n");
fprintf(stdout, "[--max <num>]\n");
fprintf(stdout, "[posicionales...]\n");
}
int main(int argc, char* argv[]){
printf("argv[0]: %s\n", argv[0]);
int opt;
int opt_index = 0;
while((opt = getopt_long(argc, argv, "hvo:m:", option_args, &opt_index)) != -1){
switch (opt){
case 'h':
print_usage(argv[0]);
exit(0);
break;
case 'v':
fprintf(stdout, "Modo verbose: ON\n");
break;
case 'o':
fprintf(stdout, "Output: %s\n", optarg);
break;
case 'm':
errno = 0;
char* errorptr = NULL;
long maximo = strtol(optarg, &errorptr, 10);
if( (errno == EINVAL)
|| (errno == ERANGE)
|| (*errorptr != 0)
|| (maximo < INT_MIN)
|| (maximo > INT_MAX)
){
fprintf(stderr, "--max debe ser un numero entero valido.\n");
exit(1);
}
printf("Maximo:%ld\n", maximo);
break;
case '?':
print_usage(argv[0]);
exit(1);
break;
}
}
fprintf(stdout, "Posicionales:\n");
for(int i = optind; i < argc; i++){
fprintf(stdout, "%s\n", argv[i]);
}
return 0;
}
BIN
View File
Binary file not shown.
Executable
BIN
View File
Binary file not shown.
+60
View File
@@ -0,0 +1,60 @@
/*
* Enunciado del Ejercicio: Modificación de Arreglos mediante Aritmética de Punteros
*
* Objetivo:
* Practicar el manejo de punteros y su relación con los arreglos estáticos en el lenguaje C,
* aplicando aritmética de punteros para recorrer y modificar los elementos almacenados en memoria.
*
* Descripción:
*
* Escribe un programa completo en C que realice las siguientes tareas:
*
* Declaración e Inicialización:
*
* Declara un arreglo estático de 5 enteros (int) e inicialízalo
* con valores arbitrarios de tu elección (por ejemplo: 5, 10, 15, 20, 25).
*
* Declara un puntero a entero (int *ptr) que apunte al inicio del arreglo.
*
* Procesamiento con Aritmética de Punteros:
*
* Utiliza un bucle (for o el de tu preferencia) para recorrer el arreglo únicamente mediante
* aritmética de punteros y el operador de indirección en la forma *(ptr + i) (o incrementando el puntero directamente).
*
* Dentro del bucle, suma 10 al valor actual almacenado en cada posición del arreglo.
*
* Salida por Consola:
*
* Muestra en pantalla los valores del arreglo antes y después de aplicar la modificación
* para verificar que el cambio se realizó correctamente en la memoria del arreglo original.
*
* Restricciones:
*
* Está prohibido utilizar la notación de corchetes (arreglo[i]) para acceder o modificar
* los elementos del arreglo dentro del bucle. Debes usar estrictamente la sintaxis de punteros (*(ptr + i)).
*/
#include <stdio.h>
#define V_LENGTH 5
void print_arr(int *);
int main(void){
int v1[V_LENGTH] = {5, 10, 15, 20, 25};
int* ptr_v1 = v1;
print_arr(ptr_v1);
for(int i=0; i<V_LENGTH; i++){
*(ptr_v1 + i) += 10;
}
print_arr(ptr_v1);
return 0;
}
void print_arr(int *arr){
printf("Array: ");
for(int i = 0; i < V_LENGTH; i++){
printf(" %d,", *(arr + i));
}
printf("\n");
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
#include <stdio.h>
int main(void) {
unsigned char flags = 0b0000010;
unsigned char FLAG_A = 1u << 0;
unsigned char FLAG_B = 1u << 1;
printf("El FLAG_A es: %08b\n", FLAG_A);
printf("El FLAG_B es: %08b\n", FLAG_B);
// encender ambos.
flags = (FLAG_A | FLAG_B);
if (flags & (FLAG_A)) {
printf("El flag A esta encendido\n");
}
if (flags & (FLAG_B)) {
printf("El flag B esta encendido\n");
}
return 0;
}
+25
View File
@@ -0,0 +1,25 @@
/*
* El objetivo es escribir un codigo donde falle al intentar modificar el contenido de un puntero
* y tambien falle al intentar modificar a donde apunta otro puntero.
*/
int main(void){
int sensor_temp = 25;
int sensor_humedad = 60;
int ipa = 192;
int ipb = 168;
const int *ptr_lectura = &sensor_temp;
int * const ptr_config = &ipa;
ptr_lectura = &sensor_humedad;
//No debo poder modificar el valor del sensor mediante el puntero.
*ptr_lectura = 44;
*ptr_config = 10;
//No debo poder cambiar a donde apunta el puntero ptr_config.
ptr_config = &ipb;
return 0;
}
BIN
View File
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
Hola, mundo
C es genial
Fin
+50
View File
@@ -0,0 +1,50 @@
#include <stdio.h>
#include <string.h>
int count_chars(char *rline);
void fix_path(char *rpath);
int main(void) {
char file_path[128];
int char_count = 0, lines_count = 0;
FILE *fp;
printf("Path:");
if (fgets(file_path, 128, stdin) == NULL) {
perror("fgets");
return 1;
}
printf("\nLeyendo archivo: %s", file_path);
fix_path(file_path);
fp = fopen(file_path, "r");
if (fp == NULL) {
perror("fopen");
return 1;
}
char lbuffer[256];
while ((fgets(lbuffer, 256, fp)) != NULL) {
char_count += count_chars(lbuffer);
lines_count++;
}
printf("Caracteres: %d\n", char_count);
printf("Lineas: %d\n", lines_count);
return 0;
}
int count_chars(char *rline) { return strlen(rline) - 1; }
void fix_path(char *rpath) {
for (int i = 0; i < 128; i++) {
if (rpath[i] == '\n') {
rpath[i] = '\0';
return;
}
}
}
BIN
View File
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
ESTE ES UN ARCHIVO
DE PRUEBAS DONDE VAMOS A EVALUAR COMO
SE COPIA ESTE CORREO HORACIODRS@GMAIL.COM
+44
View File
@@ -0,0 +1,44 @@
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *src = "src-test.txt";
char *dst = "dst-test.txt";
if (argc == 3) {
src = strdup(argv[1]);
dst = strdup(argv[2]);
} else {
fprintf(stderr, "Argumentos invalidos.\n");
return 1;
}
FILE *fpsrc, *fpdst;
fpsrc = fopen(src, "r");
if (fpsrc == NULL) {
perror("fopen");
return 1;
}
fpdst = fopen(dst, "w");
if (fpdst == NULL) {
perror("fopen");
return 1;
}
int c;
while ((c = fgetc(fpsrc)) && c != EOF) {
fputc(toupper(c), fpdst);
}
fclose(fpsrc);
fclose(fpdst);
printf("Archivo duplicado en mayusculas con exito en: %s\n", dst);
return 0;
}
+3
View File
@@ -0,0 +1,3 @@
Este es un archivo
de pruebas donde vamos a evaluar como
se copia este correo horaciodrs@gmail.com
Binary file not shown.
+166
View File
@@ -0,0 +1,166 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_error.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_log.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_video.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void render_image(SDL_Renderer *ref_renderer, unsigned char *data,
unsigned long w, unsigned long h);
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Argumentos invalidos\n");
return 1;
}
char *fname;
fname = strdup(argv[1]);
if (fname == NULL) {
perror("strdup");
return 1;
}
FILE *pfile = fopen(fname, "rb");
if (pfile == NULL) {
perror("fopen");
fclose(pfile);
return 1;
}
uint8_t *header = NULL;
header = malloc(sizeof(uint8_t) * 3);
printf("El sizeof de header es: %d\n", (int)sizeof(header));
if (fread(header, sizeof(uint8_t) * 2, 1, pfile) == 0) {
perror("fread");
fclose(pfile);
return 1;
}
header[3] = '\0';
int w, h, s;
if (fscanf(pfile, " %d %d %d", &w, &h, &s) != 3) {
fprintf(stderr, "Error leyendo las caracteristicas de la imagen.\n");
fclose(pfile);
return 1;
}
fprintf(stdout, "Header readed: %s\n", (unsigned char *)header);
fprintf(stdout,
"Las dimensiones son %dx%d pixeles y una escala maxima de color de "
"%d\n",
w, h, s);
// consumo un caracter mas y queda listo para leer los datos de los pixeles.
fgetc(pfile);
unsigned char *data = NULL;
unsigned long pixel_count = w * h;
data = malloc(sizeof(data) * pixel_count * 3);
if (data == NULL) {
perror("malloc");
fclose(pfile);
return 1;
}
unsigned long readed =
fread(data, sizeof(unsigned char) * 3, pixel_count, pfile);
fprintf(stdout, "%zu pixeles leidos de %zu\n", readed, pixel_count);
if (readed != pixel_count) {
fprintf(stderr, "Error leyendo datos del archivo\n");
free(data);
fclose(pfile);
}
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
perror("SLD_Init");
free(data);
fclose(pfile);
return 1;
}
SDL_Window *window =
SDL_CreateWindow("Visor de imagenes", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, w, h, SDL_WINDOW_SHOWN);
if (window == NULL) {
SDL_Log("SLD_WINDOW: %s", SDL_GetError());
SDL_Quit();
free(data);
fclose(pfile);
return 1;
}
SDL_Renderer *renderer = SDL_CreateRenderer(
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer) {
SDL_Log("No se pudo crear el renderizador: %s", SDL_GetError());
SDL_DestroyWindow(window);
SDL_Quit();
free(data);
fclose(pfile);
return 1;
}
bool running = true;
SDL_Event ev;
while (running) {
while (SDL_PollEvent(&ev)) {
if (ev.type == SDL_QUIT) {
running = 0; // Si hacen clic en la "X", salimos
}
if (ev.type == SDL_KEYDOWN) {
// Si presionan Escape, salimos
if (ev.key.keysym.sym == SDLK_ESCAPE) {
running = 0;
}
}
}
SDL_RenderClear(renderer);
render_image(renderer, data, w, h);
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
free(data);
fclose(pfile);
return 0;
}
void render_image(SDL_Renderer *ref_renderer, unsigned char *rdata,
unsigned long int w, unsigned long h) {
unsigned long offset = 0;
for (unsigned long cury = 0; cury < h; cury++) {
for (unsigned long curx = 0; curx < w; curx++) {
uint8_t r = *(rdata + offset++);
uint8_t g = *(rdata + offset++);
uint8_t b = *(rdata + offset++);
SDL_SetRenderDrawColor(ref_renderer, r, g, b, 255);
SDL_RenderDrawPoint(ref_renderer, curx, cury);
}
}
}
BIN
View File
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
Se encontraron 3 registros invalidos.
Iniciando reporte.
Procesando 128 registros.
Reporte generado correctamente.
+1
View File
@@ -0,0 +1 @@
Se encontraron 3 registros invalidos.
+7
View File
@@ -0,0 +1,7 @@
Permissions Size User Date Modified Name
.rw-r--r-- 38 horacio 28 Oct 10:58 errores.txt
.rw-r--r-- 0 horacio 28 Oct 11:07 listado.txt
.rw-r--r-- 271 horacio 28 Oct 10:55 main.c
.rwxr-xr-x 16k horacio 28 Oct 10:56 programa
.rw-r--r-- 24 horacio 28 Oct 11:04 salida.txt
.rw-r--r-- 19 horacio 28 Oct 11:00 test.txt
+9
View File
@@ -0,0 +1,9 @@
#include <stdio.h>
int main(void){
fprintf(stdout, "Iniciando reporte.\n");
fprintf(stdout, "Procesando 128 registros.\n");
fprintf(stdout, "Reporte generado correctamente.\n");
fprintf(stderr, "Se encontraron 3 registros invalidos.\n");
return 1;
}
BIN
View File
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
Iniciando reporte.
Procesando 128 registros.
Reporte generado correctamente.
+2
View File
@@ -0,0 +1,2 @@
test
segunda linea
+16
View File
@@ -0,0 +1,16 @@
#include <stdio.h>
int main(void){
int c, count = 0, countbr = 0;
while((c = getchar()) != EOF){
if(c == '\n'){
countbr++;
}
count++;
}
fprintf(stdout, "Se leyeron %d bytes\n", count);
fprintf(stdout, "Se leyeron %d <br> \n", countbr);
return 0;
}
BIN
View File
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
este es un archivo
de texto
+42
View File
@@ -0,0 +1,42 @@
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
int count_words(char*);
int main(void){
char buffer[50];
int total_words = 0;
while((fgets(buffer, sizeof(buffer), stdin)) != NULL){
total_words += count_words(buffer);
}
printf("El total de palabras es: %d\n", total_words);
return 0;
}
int count_words(char* line){
int palabras = 0;
bool hay_espacio = false;
printf("line: %s", line);
if((line[0] != ' ') && strlen(line) > 0){
palabras++;
}
for(int i = 0; i < strlen(line); i++){
if(line[i] == ' '){
hay_espacio = true;
}
if((line[i] != ' ') && (line[i] != '\n') && (hay_espacio)){
palabras++;
hay_espacio = false;
}
}
return palabras;
}
BIN
View File
Binary file not shown.
+32
View File
@@ -0,0 +1,32 @@
#include <stdio.h>
void clear_buffer(void);
int main(void) {
int edad = 0;
char buffer[20];
printf("Ingresar una edad:");
scanf("%d", &edad);
clear_buffer();
printf("\nIngresar un nombre:");
fgets(buffer, 20, stdin);
printf("\n====================================\n");
printf("La edad ingresada es: %d \n", edad);
printf("El nombre ingresado es: %s \n", buffer);
return 0;
}
void clear_buffer(void) {
int c = 0;
while ((c = getchar()) && c != '\n' && c != EOF) {
}
}
BIN
View File
Binary file not shown.
+50
View File
@@ -0,0 +1,50 @@
#include <stdio.h>
typedef struct Document {
int id;
char active;
char name[50];
} Document;
void leer_id(Document *);
void leer_nombre(Document *);
void imprimir(Document *);
int main(void) {
Document hdr;
leer_id(&hdr);
leer_nombre(&hdr);
imprimir(&hdr);
return 0;
}
void leer_id(Document *ref) {
int rid = -1;
char c;
char ractive = 'n';
printf("Entrada:");
scanf("%d", &rid);
ref->id = rid;
while ((c = getchar()) && c != '\n' && c != EOF) {
if ((c == 's') || (c == 'S')) {
ractive = 's';
}
}
ref->active = ractive;
printf("\n");
}
void leer_nombre(Document *ref) {
printf("Nombre:");
fgets(ref->name, 50, stdin);
}
void imprimir(Document *ref) {
printf("=======================================\n");
printf("ID: %d \n", ref->id);
printf("ACTIVE: %c \n", ref->active);
printf("NOMBRE: %s \n", ref->name);
}
BIN
View File
Binary file not shown.
+13
View File
@@ -0,0 +1,13 @@
#include "stringv.h"
#include <stdio.h>
int main(void) {
StringView s1 = sv_from_cstr("Hola mundo con string view");
StringView s2 = sv_from_cstr("😊");
StringView last = sv_right(&s1, 4);
printf("El string view contiene: %s\n", s2.text);
printf("El length es: %zu\n", s2.lenght);
printf("last tiene:%.*s\n", (int)last.lenght, last.text);
return 0;
}
BIN
View File
Binary file not shown.
+60
View File
@@ -0,0 +1,60 @@
#include "stringv.h"
#include <stddef.h>
#include <stdio.h>
#include <string.h>
/*
* @brief Create a string view from a const char* and set their lenght
*
* @details This string view provides a view of a string. It does not allocate
* memory, therefore, it lives only while the source is alive. The
* lenght is a bytes number of the string. It does not represents the number of
* characters.
*
* @param str pointer from the original string. It can be NULL.
*
* @return a StringView with the view of original string with corresponding
* lenght.
*/
#define STRING_VIEW_NULL (StringView){.text = NULL, .lenght = 0}
StringView sv_from_cstr(const char *str) {
if (str == NULL) {
return STRING_VIEW_NULL;
}
return (StringView){.text = (char *)str, .lenght = strlen(str)};
}
StringView sv_left(const StringView *sv, size_t l) {
if (sv == NULL) {
perror("sv_left: pointer is NULL\n");
return STRING_VIEW_NULL;
}
if (sv->text == NULL) {
perror("sv_left: text pointer is NULL\n");
return STRING_VIEW_NULL;
}
if (sv->lenght < l) {
perror("sv_left: text length is less than specified length\n");
return STRING_VIEW_NULL;
}
return (StringView){.text = sv->text, .lenght = l};
}
StringView sv_right(const StringView *sv, size_t r) {
if (sv == NULL) {
perror("sv_right: pointer is NULL\n");
return STRING_VIEW_NULL;
}
if (sv->text == NULL) {
perror("sv_right: text pointer is NULL\n");
return STRING_VIEW_NULL;
}
if (sv->lenght < r) {
perror("sv_right: text length is less than specified length\n");
return STRING_VIEW_NULL;
}
size_t nlength = sv->lenght - r;
return (StringView){.text = sv->text + nlength, .lenght = r};
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef STRING_VIEW_H
#define STRING_VIEW_H
#include <stddef.h>
typedef struct StringView {
char *text;
size_t lenght;
} StringView;
StringView sv_from_cstr(const char *);
StringView sv_left(const StringView *sv, size_t l);
StringView sv_right(const StringView *sv, size_t l);
#endif
+69
View File
@@ -0,0 +1,69 @@
/*
* EJERCICIO DE LENGUAJE C: STRUCTS Y PUNTEROS
*
* 1. Define una struct llamada Coordenada que contenga dos campos enteros: x e y.
* 2. Implementa una función constructora llamada Coordenada* crear_coordenada(int x, int y) que:
* - Reserve memoria dinámicamente utilizando malloc para una nueva estructura Coordenada.
* - Verifique si la reserva de memoria fue exitosa.
* - Inicialice sus campos x e y con los valores pasados como parámetros.
* - Devuelva el puntero a la estructura creada.
* 3. En la función main:
* - Utiliza la función constructora para crear una coordenada.
* - Muestra por pantalla los valores iniciales utilizando el operador flecha (->).
* - Modifica los valores de los campos de la estructura utilizando también el operador flecha (->).
* - Muestra los nuevos valores por pantalla.
* - Libera la memoria reservada dinámicamente utilizando free para evitar pérdidas de memoria.
*/
#include <stdio.h>
#include <stdlib.h>
typedef struct{
int x;
int y;
}Coordenada;
Coordenada* crear_coordenada(int x, int y);
void modificar_coordenada(Coordenada**, int x, int y);
void imprimir_coordenada(Coordenada**);
int main(void){
Coordenada* cord = crear_coordenada(10, 20);
imprimir_coordenada(&cord);
modificar_coordenada(&cord, 30, 40);
imprimir_coordenada(&cord);
free(cord);
return 0;
}
Coordenada* crear_coordenada(int x, int y){
Coordenada* new_cord = malloc(sizeof(Coordenada));
if(new_cord == NULL){
perror("No se pudo crear la coordenada\n");
exit(1);
}
new_cord->x = x;
new_cord->y = y;
return new_cord;
}
void modificar_coordenada(Coordenada** cord_address, int x, int y){
if((*cord_address) == NULL){
perror("No se ha proporcionado una coordenada para modificar\n");
exit(1);
}
(*cord_address)->x = x;
(*cord_address)->y = y;
}
void imprimir_coordenada(Coordenada** a){
printf("Coordenada(%d, %d);\n", (*a)->x, (*a)->y);
}
BIN
View File
Binary file not shown.
+92
View File
@@ -0,0 +1,92 @@
/*******************************************************************************
* TRABAJO PRÁCTICO / EJERCICIO INTEGRADOR: GESTIÓN DE PUNTEROS Y ESTRUCTURAS
*
* OBJETIVO:
* El propósito de este ejercicio es consolidar el manejo de estructuras,
* aritmética de punteros, indirección y diseño de funciones conpaso por referencia
* en el lenguaje C, evitando el uso de código resuelto y enfocándose en la
* especificación formal del problema.
*
* CONSIGNA:
* Desarrolle un programa en Lenguaje C que cumpla rigurosamente con los
* siguientes requerimientos técnicos y estructurales:
*
* 1. Definición del Tipo de Dato (Struct con Punteros Internos):
* - Defina una estructura denominada `Persona` que contenga dos campos:
* * Un puntero a caracteres (`char *nombre`) destinado a almacenar
* dinámicamente o mediante literales la identificación de la persona.
* * Un número entero (`int edad`) que represente los años de la persona.
*
* 2. Estructura de Almacenamiento (Arreglo de Estructuras):
* - En la función principal (`main`), declare un arreglo estático con
* capacidad exactamente para 3 elementos de tipo `Persona`.
* - Inicialice cada uno de los elementos del arreglo asignando valores
* válidos tanto para el puntero `nombre` como para la `edad`.
* - Implemente una función específica (cuya firma y diseño quedan a su cargo)
* que reciba como parámetro el puntero al inicio del arreglo de estructuras
* y su tamaño, y se encargue de recorrerlo sistemáticamente para
* imprimir en la consola los datos de todos sus elementos.
*
* 3. Encapsulamiento y Modificación (Funciones Modificadoras / Setters):
* - Escriba una función modular de tipo `void` llamada `cambiar_edad`
* cuya firma obligatoria sea:
* `void cambiar_edad(Persona *p, int nueva_edad)`
* - Dicha función debe recibir un puntero a una estructura `Persona`
* para actualizar el campo correspondiente de forma segura mediante
* el operador de indirección adecuado, reflejando el cambio en el ámbito
* del programa principal.
*
* 4. Pautas de Entrega y Validación:
* - El programa debe compilar sin errores ni advertencias bajo un estándar
* moderno de C (por ejemplo, -std=c11 o superior).
* - No se deben utilizar variables globales; toda la comunicación entre
* funciones debe realizarse mediante parámetros y punteros.
*******************************************************************************/
#include <stdio.h>
#define P_LENGTH 3
typedef struct{
char* nombre;
int edad;
}Persona;
void listar_personas(Persona*, int length);
void cambiar_edad(Persona *p, int nueva_edad);
int main(void){
Persona personas[P_LENGTH] = {
{
.nombre = "Horacio",
.edad = 43
},
{
.nombre = "Raquel",
.edad = 44
}, {
.nombre = "Luna",
.edad = 15
}
};
listar_personas(personas, P_LENGTH);
cambiar_edad(personas, 21);
cambiar_edad(personas+1, 22);
cambiar_edad(personas+2, -5);
listar_personas(personas, P_LENGTH);
return 0;
}
void listar_personas(Persona* p, int length){
for(int i=0; i < length; i++){
printf("%s \t %d \n", (p+i)->nombre, (p+i)->edad);
}
}
void cambiar_edad(Persona *p, int nueva_edad){
p->edad = nueva_edad;
}
BIN
View File
Binary file not shown.
+347
View File
@@ -0,0 +1,347 @@
/* =========================================================================
* PROYECTO: Sistema de Registro y Telemetría para el Nanosatélite "Andrómeda-1"
* -------------------------------------------------------------------------
* CONTEXTO:
* La Agencia Espacial Experimental está desarrollando el software de a bordo
* para su próximo nanosatélite. El sistema necesita gestionar memoria de
* forma dinámica y eficiente debido a los recursos limitados del microcontrolador
* y a la naturaleza impredecible de las ráfagas de datos en órbita.
*
* OBJETIVO:
* Diseñar e implementar un programa en lenguaje C que resuelva los siguientes
* módulos de gestión de memoria dinámica requeridos por la estación de tierra:
*
* 1. Vector Dinámico Básico (Canales de Sensores):
* Pide al operador un número N de canales activos, utiliza malloc para
* reservar un arreglo de enteros con las lecturas iniciales, muéstralo en
* consola y libera correctamente la memoria con free al terminar.
*
* 2. Cadenas Dinámicas (Identificadores de Misión):
* Escribe una función que reciba una cadena estática (char *) con el nombre
* de código del satélite, calcule su longitud exacta, reserve espacio
* dinámicamente con malloc, copie la cadena y devuelva el nuevo puntero
* dinámico para ser registrado en los logs del sistema.
*
* 3. Estructura Dinámica (Buffer de Archivos de Telemetría):
* Define una struct Archivo que contenga dos campos: size_t tamano y
* uint8_t *datos. Escribe una función que reciba el tamaño deseado y reserve
* memoria dinámicamente tanto para la estructura en sí como para su búfer
* interno de datos binarios.
*
* 4. Redimensionamiento con realloc (Stream de Datos en Tiempo Real):
* Crea una rutina que lea números enteros (paquetes de telemetría) desde
* la consola de forma indefinida hasta una condición de parada. Almacénalos
* en un arreglo dinámico que duplique su tamaño cada vez que se llene
* utilizando realloc para optimizar el almacenamiento.
*
* 5. Estructuras Anidadas Dinámicas (Subsistemas del Satélite):
* Crea una estructura principal (por ejemplo, MisionEspacial) que contenga
* un puntero a un arreglo dinámico de subestructuras (representando varios
* subsistemas como energía, propulsión y comunicación). Asegúrate de
* implementar un protocolo estricto de liberación en orden inverso
* (primero las subestructuras internas y sus recursos, luego el arreglo
* principal) para evitar fugas de memoria (memory leaks) críticas en órbita.
*
* REQUISITOS TÉCNICOS:
* - Uso obligatorio de las funciones de <stdlib.h> (malloc, realloc, free).
* - Validación de punteros (verificar que no sean NULL tras cada reserva).
* - Código limpio, modularizado en funciones y bien comentado.
* ========================================================================= */
#include <stddef.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct{
size_t size;
uint8_t *datos;
}TelemetryFile;
typedef struct{
TelemetryFile base;
size_t offset;
}TelemetryArray;
typedef struct{
int n_chanels;
int* chanels;
TelemetryFile* telemetry_file;
TelemetryArray telemetry_array;
}ProgramData;
void print_menu(ProgramData *);
void print_data(ProgramData *);
void get_nchanels(ProgramData *);
void get_chanels(ProgramData *);
void get_telemetry_file(ProgramData *);
TelemetryFile* telemetry_file_create(size_t fs);
void telemetry_file_free(ProgramData *);
void get_telemetry_data(ProgramData *);
bool telemetry_array_is_full(ProgramData *);
void telemetry_array_alloc(ProgramData *);
void telemetry_array_free(ProgramData *);
void telemetry_array_set(ProgramData *, uint8_t data);
void free_data(ProgramData*);
void pause(void);
int main(void){
ProgramData data = {
.n_chanels = 0,
.chanels = NULL,
.telemetry_file = NULL,
.telemetry_array = {
.base = {
.size = 0,
.datos = NULL
},
.offset = 0
}
};
print_menu(&data);
free_data(&data);
return 0;
}
void print_menu(ProgramData* ref_data){
int option = 0;
int exit = 0;
system("clear");
printf("MENU DE OPCIONES\n");
printf("\t 1- Cargar canales\n");
printf("\t 2- Mostrar Datos\n");
printf("\t 3- Crear archivo de telemetría\n");
printf("\t 4- Recibir telemetria\n");
printf("\t 5- Salir\n");
scanf("%d", &option);
switch (option) {
case 1:
get_nchanels(ref_data);
get_chanels(ref_data);
break;
case 2:
print_data(ref_data);
break;
case 3:
get_telemetry_file(ref_data);
break;
case 4:
get_telemetry_data(ref_data);
break;
case 5:
exit = 1;
break;
}
if(!exit){
print_menu(ref_data);
}
}
void get_telemetry_data(ProgramData *ref_data){
system("clear");
printf("RECEPCION DE TELEMETRIA\n");
uint8_t read_data;
do{
printf("\nIngresar valor (0 para terminar):");
scanf("%" SCNu8, &read_data);
//printf("Se ha leido: %" SCNu8 "\n", read_data);
if(read_data > 0){
telemetry_array_set(ref_data, read_data);
}
}while(read_data > 0);
}
bool telemetry_array_is_full(ProgramData *ref_data){
uint8_t* start_add = ref_data->telemetry_array.base.datos;
if(start_add == NULL){
return true;
}
uint8_t* end_add = start_add + ref_data->telemetry_array.base.size;
uint8_t* test_add = start_add + ref_data->telemetry_array.offset;
return test_add >= (end_add);
}
void telemetry_array_alloc(ProgramData *ref_data){
//Si esta vacio reserva espacio para uno.
if(ref_data->telemetry_array.base.datos == NULL){
ref_data->telemetry_array.base.datos = malloc(sizeof(uint8_t));
if(ref_data->telemetry_array.base.datos == NULL){
perror("Error asignando memoria para streaming de telemetria.");
exit(1);
}
ref_data->telemetry_array.base.size = 1;
ref_data->telemetry_array.offset = 0;
return;
}
uint8_t* result = realloc(ref_data->telemetry_array.base.datos, ref_data->telemetry_array.base.size * 2);
if(result == NULL){
perror("Error reasignando memoria para streaming de telemetria.");
exit(1);
}
ref_data->telemetry_array.base.datos = result;
ref_data->telemetry_array.base.size *= 2;
printf("La nueva capacidad es: %zu \n", ref_data->telemetry_array.base.size);
}
void telemetry_array_free(ProgramData *ref_data){
if(ref_data->telemetry_array.base.datos == NULL){
free(ref_data->telemetry_array.base.datos);
ref_data->telemetry_array.base.datos = NULL;
}
}
void telemetry_array_set(ProgramData *ref_data, uint8_t data){
if(telemetry_array_is_full(ref_data)){
printf("Esta lleno, vamos a agrandar \n");
telemetry_array_alloc(ref_data);
}
*(ref_data->telemetry_array.base.datos + ref_data->telemetry_array.offset) = data;
ref_data->telemetry_array.offset += 1;
}
void get_telemetry_file(ProgramData *ref_data){
size_t fs;
system("clear");
printf("CREAR ARCHIVO DE TELEMETRIA\n");
printf("\tEnter de size in bytes:");
scanf("%zu", &fs);
telemetry_file_free(ref_data);
ref_data->telemetry_file = telemetry_file_create(fs);
}
void telemetry_file_free(ProgramData *ref_data){
if(ref_data->telemetry_file != NULL){
if(ref_data->telemetry_file->datos != NULL){
free(ref_data->telemetry_file->datos);
ref_data->telemetry_file->datos = NULL;
}
free(ref_data->telemetry_file);
ref_data->telemetry_file = NULL;
}
}
TelemetryFile* telemetry_file_create(size_t fs){
TelemetryFile* telemetry_file = malloc(sizeof(TelemetryFile));
if(telemetry_file == NULL){
perror("Error creating telemetry file.");
exit(1);
}
telemetry_file->size = fs;
telemetry_file->datos = malloc(sizeof(uint8_t)*fs);
if(telemetry_file->datos == NULL){
free(telemetry_file);
telemetry_file = NULL;
perror("Error allocating memory for the buffer to telemetry file.");
exit(1);
}
return telemetry_file;
}
void get_nchanels(ProgramData* ref_data){
system("clear");
int n_chanels = 0;
printf("Cantidad de canales: ");
scanf("%d", &n_chanels);
ref_data->n_chanels = n_chanels;
}
void get_chanels(ProgramData* ref_data){
if(ref_data->chanels != NULL){
free(ref_data->chanels);
ref_data->chanels = NULL;
}
ref_data->chanels = malloc(sizeof(int)*ref_data->n_chanels);
if(ref_data->chanels == NULL){
perror("No se pudo reservar memoria para los canales.");
exit(1);
}
for(int i=0; i < ref_data->n_chanels; i++){
int chanel;
printf("Frecuacia en Mhz del canal%d:", i);
scanf("%d", &chanel);
*(ref_data->chanels + i) = chanel;
}
}
void print_data(ProgramData* ref_data){
system("clear");
printf("DATOS DEL PROGRAMA\n");
printf("Cantidad de canales: %d\n", ref_data->n_chanels);
for (int i=0; i < ref_data->n_chanels; i++){
printf("\t CH %d Mhz\n", *(ref_data->chanels + i));
}
if(ref_data->telemetry_file != NULL){
printf("Archivo de telemetria creado. Size: %zu bytes.\n", ref_data->telemetry_file->size);
}
printf("Datos de telemetria: ");
for(size_t i=0; i < ref_data->telemetry_array.offset; i++){
printf("%" SCNu8 ", ", *(ref_data->telemetry_array.base.datos + i));
}
if(ref_data->telemetry_array.base.size == 0){
printf("No hay datos.");
}
printf("\n");
pause();
}
void free_data(ProgramData* ref_data){
if(ref_data->chanels){
free(ref_data->chanels);
}
telemetry_file_free(ref_data);
telemetry_array_free(ref_data);
}
void pause(void){
// 1. Limpiamos el búfer por si quedó algún caracter pendiente del scanf anterior
int c;
while ((c = getchar()) != '\n' && c != EOF);
// 2. Mostramos el mensaje y esperamos a que el usuario presione Enter de verdad
printf("Presione Enter para continuar...\n");
getchar();
}
BIN
View File
Binary file not shown.
+28
View File
@@ -0,0 +1,28 @@
#include <stdio.h>
void swap(char **, char**);
void swap_int(int *, int *);
int main(void){
char *s1 = "Hola";
char *s2 = "Mundo";
int n1 = 10;
int n2 = 20;
swap(&s1, &s2);
swap_int(&n1, &n2);
printf("s1: %s s2: %s\n", s1, s2);
printf("n1: %d n2: %d\n", n1, n2);
return 0;
}
void swap(char **s1, char **s2){
char *aux = *s1;
*s1 = *s2;
*s2 = aux;
}
void swap_int(int* n1, int* n2){
int aux = *n1;
*n1 = *n2;
*n2 = aux;
}
BIN
View File
Binary file not shown.
+48
View File
@@ -0,0 +1,48 @@
/* ============================================================================
* Ejercicio: Modificación de un Puntero a través de un Puntero a Puntero (int **)
* ============================================================================
* Objetivo:
* Comprender el uso de punteros a punteros para modificar la dirección a la
* que apunta un puntero original dentro de una función (pasaje por referencia).
*
* Descripción:
* Escribe un programa en C que cumpla con los siguientes requisitos:
*
* 1. Define dos variables enteras, por ejemplo: `int x = 10;` e `int y = 20;`.
* 2. Declara un puntero simple a entero, por ejemplo: `int *ptr = &x;`.
* 3. Crea una función llamada `cambiar_puntero` que reciba un puntero a puntero
* (`int **p_ref`) y la dirección de otra variable entera (`int *nueva_dir`).
* La función debe actualizar el puntero original para que ahora apunte a
* esa nueva dirección (`*p_ref = nueva_dir;`).
* 4. En la función `main`, imprime el valor al que apunta `ptr` antes de llamar
* a la función.
* 5. Llama a `cambiar_puntero` pasando la dirección de `ptr` (`&ptr`) y la
* dirección de `y` (`&y`).
* 6. Vuelve a imprimir el valor al que apunta `ptr` después de la llamada para
* verificar que ahora apunta a `y` (su valor debe ser 20).
*
* Restricciones:
* - Debes utilizar obligatoriamente el operador de indirección doble (**).
* - No olvides validar los conceptos de memoria y desreferenciación.
* ============================================================================ */
#include <stdio.h>
void cambiar_puntero(int**, int*);
int main(void){
int x = 20, y = 30;
int* ptr = &x;
printf("contenido de ptr = %d \n", *ptr);
cambiar_puntero(&ptr, &y);
printf("contenido de ptr = %d\n", *ptr);
}
void cambiar_puntero(int** ptr, int *address){
*ptr = address;
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.