Prerrequisitos y Entorno de Ejecución Requerido¶
Para compilar y verificar las soluciones de este módulo bajo el estándar C11 estricto de cátedra, se requiere:
Compilador C11: GCC 9+ o Clang 11+ configurado con flags
-Wall -Wextra -Werror -pedantic -std=c11.Entorno POSIX: Linux o WSL con utilidades estándar y análisis dinámico de memoria.
Herramientas de Verificación: Valgrind (memcheck) y AddressSanitizer (
-fsanitize=address,undefined) para auditar la liberación simétrica, evitar memory leaks y detectar punteros colgantes (dangling pointers).Conocimientos Previos: Ciclo de vida en Heap (
malloc,calloc,realloc,free), punteros dobles (T **), estructuras heterogéneas, copia profunda y patrones de rollback.
Objetivos Pedagógicos y Competencias (Taxonomía de Bloom)¶
Nivel 2 (Comprensión): Diferenciar memoria dinámica simple de estructuras jerárquicas y matrices dentadas.
Nivel 3 (Aplicación): Implementar constructores y destructores defensivos simétricos con limpieza total ante fallos.
Nivel 4 (Análisis): Evaluar esquemas de asignación (contigua vs vector de punteros) y protocolos de anulación de punteros dobles.
Andamiaje Progresivo: Ejercicios andamiados con contratos formales (precondiciones/postcondiciones), tablas de vectores de prueba y suites ejecutables con
assert().
Capítulos de Apunte Correspondientes¶
Cuestiones de Estilo Aplicables¶
Manejo seguro de punteros: Es mandatorio liberar en el orden inverso a la asignación (de adentro hacia afuera) y establecer los punteros en
NULLtras su liberación para prevenir dangling pointers (ver 0x3002h: Liberá siempre la memoria dinámica y asigná NULL al puntero para mitigar punteros colgantes).Verificación de malloc: Siempre se debe validar el resultado de las llamadas a
malloc,callocyreallocantes de realizar operaciones de lectura o escritura.
Estructuras con Punteros¶
Ejercicio 2.06.01 - Creación y Destrucción de Persona ⭐⭐☆☆☆¶
(ej_b2_c06_03)=
### Ejercicio 2.06.03 - Clonación Profunda ⭐⭐☆☆☆
:::{exercise}
:label: ej_b2_c06_03_clonacion_profunda
Implementá una función que cree una copia completamente independiente de una
persona:
```c
persona_t *persona_clonar(const persona_t *original);
```
La copia debe tener su propia memoria asignada para `nombre` y `apellido`, sin
compartir punteros con el original.
::::{solution}
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
typedef struct {
char *nombre;
char *apellido;
int edad;
} persona_t;
persona_t *persona_crear(const char *nombre, const char *apellido, int edad) {
if (nombre == NULL || apellido == NULL || edad < 0) return NULL;
persona_t *p = (persona_t *)malloc(sizeof(persona_t));
if (p == NULL) return NULL;
p->nombre = (char *)malloc(strlen(nombre) + 1);
if (p->nombre == NULL) { free(p); return NULL; }
strcpy(p->nombre, nombre);
p->apellido = (char *)malloc(strlen(apellido) + 1);
if (p->apellido == NULL) { free(p->nombre); free(p); return NULL; }
strcpy(p->apellido, apellido);
p->edad = edad;
return p;
}
void persona_destruir(persona_t **ptr_persona) {
if (ptr_persona == NULL || *ptr_persona == NULL) return;
free((*ptr_persona)->nombre);
free((*ptr_persona)->apellido);
free(*ptr_persona);
*ptr_persona = NULL;
}
persona_t *persona_clonar(const persona_t *original) {
if (original == NULL) {
return NULL;
}
return persona_crear(original->nombre, original->apellido, original->edad);
}
int main(void) {
persona_t *p1 = persona_crear("Ada", "Lovelace", 36);
assert(p1 != NULL);
persona_t *p2 = persona_clonar(p1);
assert(p2 != NULL);
assert(p2 != p1);
assert(p2->nombre != p1->nombre);
assert(p2->apellido != p1->apellido);
assert(strcmp(p2->nombre, p1->nombre) == 0);
assert(strcmp(p2->apellido, p1->apellido) == 0);
assert(p2->edad == p1->edad);
persona_destruir(&p1);
assert(p1 == NULL);
/* p2 debe seguir intacto tras la destrucción de p1 */
assert(strcmp(p2->nombre, "Ada") == 0);
persona_destruir(&p2);
assert(p2 == NULL);
assert(persona_clonar(NULL) == NULL);
return 0;
}
```
(ej_b2_c06_04)=
### Ejercicio 2.06.04 - Estructura con Múltiples Niveles ⭐⭐⭐☆☆
Implementar constructor y destructor para esta estructura anidada:
```{code-block} c
:linenos:
typedef struct
{
char *calle;
char *ciudad;
int codigo_postal;
} direccion_t;
typedef struct
{
char *nombre;
direccion_t *direccion;
char **telefonos; // Array de cadenas
size_t n_telefonos;
} contacto_t;
contacto_t *contacto_crear(const char *nombre, const char *calle,
const char *ciudad, int codigo_postal);
void contacto_destruir(contacto_t **ptr_contacto);
```
<!-- {code-block} c -->
**Desafío:** Manejar correctamente tres niveles de asignación: la estructura
principal, la dirección anidada, y el array dinámico de cadenas.
---
## Manejo de Errores en Cadena
(ej_b2_c06_05)=
### Ejercicio 2.06.05 - Rollback Completo ⭐⭐☆☆☆
Escribir una función que asigne memoria para una estructura de estudiante con
cursos:
```{code-block} c
:linenos:
typedef struct
{
char *nombre;
char **cursos;
int *notas;
size_t n_cursos;
} estudiante_t;
```
<!-- {code-block} c -->
Si la asignación de `notas` falla después de haber asignado `nombre` y `cursos`,
la función debe liberar `nombre` y `cursos` antes de retornar `NULL` para evitar
fugas de memoria.
(ej_b2_c06_06)=
### Ejercicio 2.06.06 - Alternativa con Goto ⭐⭐☆☆☆
Implementar la función del ejercicio anterior estructurando la liberación de
recursos en una sección de limpieza al final de la función mediante `goto`, como
se describe en las buenas prácticas de la cátedra.
---
## Matrices Dinámicas
(ej_b2_c06_07)=
### Ejercicio 2.06.07 - Matriz Dentada (Array de Punteros) ⭐⭐⭐☆☆
:::{exercise}
:label: ej_b2_c06_07_matriz_dentada
Implementá funciones para crear y liberar una matriz dentada donde cada fila se
aloja como un bloque independiente:
```c
int **crear_matriz_dentada(size_t filas, size_t columnas);
void liberar_matriz_dentada(int ***ptr_matriz, size_t filas);
```
**Requisitos:**
- Si `filas == 0` o `columnas == 0`, retornar `NULL`.
- En caso de fallo de `malloc` en una fila intermedia, liberar las filas previas y el array de punteros (*rollback* completo).
- `liberar_matriz_dentada` debe poner el puntero original en `NULL`.
::::{solution}
```c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int **crear_matriz_dentada(size_t filas, size_t columnas) {
if (filas == 0 || columnas == 0) {
return NULL;
}
int **m = (int **)malloc(filas * sizeof(int *));
if (m == NULL) {
return NULL;
}
for (size_t i = 0; i < filas; ++i) {
m[i] = (int *)malloc(columnas * sizeof(int));
if (m[i] == NULL) {
/* Rollback de filas previas */
for (size_t j = 0; j < i; ++j) {
free(m[j]);
}
free(m);
return NULL;
}
}
return m;
}
void liberar_matriz_dentada(int ***ptr_matriz, size_t filas) {
if (ptr_matriz == NULL || *ptr_matriz == NULL) {
return;
}
int **m = *ptr_matriz;
for (size_t i = 0; i < filas; ++i) {
free(m[i]);
}
free(m);
*ptr_matriz = NULL;
}
int main(void) {
size_t filas = 3;
size_t cols = 4;
int **m = crear_matriz_dentada(filas, cols);
assert(m != NULL);
for (size_t i = 0; i < filas; ++i) {
for (size_t j = 0; j < cols; ++j) {
m[i][j] = (int)(i * 10 + j);
}
}
assert(m[0][0] == 0);
assert(m[1][2] == 12);
assert(m[2][3] == 23);
liberar_matriz_dentada(&m, filas);
assert(m == NULL);
/* Casos límite */
assert(crear_matriz_dentada(0, 5) == NULL);
assert(crear_matriz_dentada(5, 0) == NULL);
liberar_matriz_dentada(&m, 0);
liberar_matriz_dentada(NULL, 5);
return 0;
}
```
(ej_b2_c06_08)=
### Ejercicio 2.06.08 - Matriz de Bloque Único (Contigua) ⭐⭐⭐☆☆
:::{exercise}
:label: ej_b2_c06_08_matriz_contigua
Implementá funciones para crear y liberar una matriz contigua en memoria,
reservando un bloque único para los datos y configurando el array de punteros a filas:
```c
int **crear_matriz_contigua(size_t filas, size_t columnas);
void liberar_matriz_contigua(int ***ptr_matriz);
```
::::{solution}
```c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int **crear_matriz_contigua(size_t filas, size_t columnas) {
if (filas == 0 || columnas == 0) {
return NULL;
}
int **m = (int **)malloc(filas * sizeof(int *));
if (m == NULL) {
return NULL;
}
int *datos = (int *)malloc(filas * columnas * sizeof(int));
if (datos == NULL) {
free(m);
return NULL;
}
for (size_t i = 0; i < filas; ++i) {
m[i] = datos + (i * columnas);
}
return m;
}
void liberar_matriz_contigua(int ***ptr_matriz) {
if (ptr_matriz == NULL || *ptr_matriz == NULL) {
return;
}
int **m = *ptr_matriz;
/* Liberar bloque de datos contiguo indexado en fila 0 */
free(m[0]);
/* Liberar arreglo de punteros */
free(m);
*ptr_matriz = NULL;
}
int main(void) {
size_t filas = 4;
size_t cols = 5;
int **m = crear_matriz_contigua(filas, cols);
assert(m != NULL);
for (size_t i = 0; i < filas; ++i) {
for (size_t j = 0; j < cols; ++j) {
m[i][j] = (int)(i + j);
}
}
assert(m[0][0] == 0);
assert(m[3][4] == 7);
/* Verificar contigüidad en memoria: m[1][0] debe ser m[0][0] + cols */
assert(&m[1][0] == &m[0][cols]);
liberar_matriz_contigua(&m);
assert(m == NULL);
assert(crear_matriz_contigua(0, 5) == NULL);
assert(crear_matriz_contigua(5, 0) == NULL);
liberar_matriz_contigua(NULL);
return 0;
}
```
(ej_b2_c06_09)=
### Ejercicio 2.06.09 - Conversión de Array Plano a Matriz ⭐⭐⭐☆☆
Implementar una función que reciba un arreglo plano (`int*`) de tamaño $N \times
M$ y retorne una estructura de punteros a filas (`int**`) que permita acceder al
mismo usando la notación `matriz[i][j]`.
---
## Optimización y Casos Prácticos
(ej_b2_c06_10)=
### Ejercicio 2.06.10 - Vector Dinámico Redimensionable con Crecimiento Seguro ⭐⭐⭐☆☆
:::{exercise}
:label: ej_b2_c06_10_vector_dinamico
:enumerator: punteros2-10
Implementá una estructura de datos `vector_dinamico_t` que gestione un búfer continuo de enteros en el Heap, duplicando su capacidad de manera segura cuando se sature:
```c
typedef struct {
int *datos;
size_t cantidad;
size_t capacidad;
} vector_dinamico_t;
vector_dinamico_t *vector_crear(size_t capacidad_inicial);
bool vector_agregar(vector_dinamico_t *v, int elemento);
bool vector_obtener(const vector_dinamico_t *v, size_t indice, int *salida);
void vector_destruir(vector_dinamico_t *v);
```
**Reglas de gestión de memoria y contratos:**
1. **Inicialización:** `vector_crear` debe asignar tanto la cabecera como el búfer subyacente. Si `capacidad_inicial == 0`, se asigna por defecto una capacidad inicial mínima de 4 elementos.
2. **Crecimiento geométrico seguro:** Cuando `cantidad == capacidad`, `vector_agregar` debe redimensionar al doble (`capacidad * 2`) usando obligatoriamente un puntero temporal intermedio para capturar el retorno de `realloc`. Si `realloc` falla, el vector debe conservar sus datos previos intactos y retornar `false`.
3. **Acceso indexado:** `vector_obtener` debe verificar que `indice < cantidad`. Si es válido, almacena el valor en `*salida` y retorna `true`; ante accesos fuera de rango o punteros nulos, retorna `false`.
4. **Destrucción limpia:** `vector_destruir` debe liberar el búfer `datos` y luego la estructura principal, tolerando llamadas defensivas con `NULL`.
#### Tabla de Vectores de Prueba Obligatorios
| Operación / Secuencia | Estado Inicial $(N / C)$ | Elemento | Retorno | Estado Final $(N / C)$ | Justificación |
| :--- | :--- | :--- | :--- | :--- | :--- |
| `vector_crear(2)` | - | - | Válido | `0 / 2` | Reserva inicial de 2 slots |
| `vector_agregar(10)` | `0 / 2` | `10` | `true` | `1 / 2` | Inserción sin reasignación |
| `vector_agregar(20)` | `1 / 2` | `20` | `true` | `2 / 2` | Saturación del búfer inicial |
| `vector_agregar(30)` | `2 / 2` | `30` | `true` | `3 / 4` | Crecimiento geométrico al doble |
| `vector_obtener(1)` | `3 / 4` | `idx=1` | `true` (`*salida=20`) | Inalterado | Lectura indexada válida |
| `vector_obtener(5)` | `3 / 4` | `idx=5` | `false` | Inalterado | Rechazo defensivo fuera de rango |
Solution to ej_b2_c06_10_vector_dinamico
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <assert.h> typedef struct { int *datos; size_t cantidad; size_t capacidad; } vector_dinamico_t; #define VECTOR_CAPACIDAD_DEFECTO 4 vector_dinamico_t *vector_crear(size_t capacidad_inicial) { if (capacidad_inicial == 0) { capacidad_inicial = VECTOR_CAPACIDAD_DEFECTO; } vector_dinamico_t *v = (vector_dinamico_t *)malloc(sizeof(vector_dinamico_t)); if (v == NULL) { return NULL; } v->datos = (int *)malloc(capacidad_inicial * sizeof(int)); if (v->datos == NULL) { free(v); return NULL; } v->cantidad = 0; v->capacidad = capacidad_inicial; return v; } bool vector_agregar(vector_dinamico_t *v, int elemento) { if (v == NULL) { return false; } if (v->cantidad >= v->capacidad) { size_t nueva_cap = v->capacidad * 2; int *temporal = (int *)realloc(v->datos, nueva_cap * sizeof(int)); if (temporal == NULL) { return false; } v->datos = temporal; v->capacidad = nueva_cap; } v->datos[v->cantidad++] = elemento; return true; } bool vector_obtener(const vector_dinamico_t *v, size_t indice, int *salida) { if (v == NULL || salida == NULL || indice >= v->cantidad) { return false; } *salida = v->datos[indice]; return true; } void vector_destruir(vector_dinamico_t *v) { if (v == NULL) { return; } free(v->datos); free(v); } int main(void) { vector_dinamico_t *v = vector_crear(2); assert(v != NULL); assert(v->cantidad == 0); assert(v->capacidad == 2); /* Inserciones y crecimiento */ assert(vector_agregar(v, 10) == true); assert(vector_agregar(v, 20) == true); assert(v->cantidad == 2 && v->capacidad == 2); /* Forzar realloc a 4 */ assert(vector_agregar(v, 30) == true); assert(v->cantidad == 3 && v->capacidad == 4); /* Lecturas indexadas */ int leido = 0; assert(vector_obtener(v, 0, &leido) == true && leido == 10); assert(vector_obtener(v, 1, &leido) == true && leido == 20); assert(vector_obtener(v, 2, &leido) == true && leido == 30); /* Fuera de rango */ assert(vector_obtener(v, 3, &leido) == false); assert(vector_obtener(v, 99, &leido) == false); assert(vector_obtener(v, 0, NULL) == false); /* Crecimiento continuo */ assert(vector_agregar(v, 40) == true); assert(vector_agregar(v, 50) == true); assert(v->cantidad == 5 && v->capacidad == 8); vector_destruir(v); vector_destruir(NULL); /* Capacidad inicial 0 -> defecto */ vector_dinamico_t *v_def = vector_crear(0); assert(v_def != NULL && v_def->capacidad == VECTOR_CAPACIDAD_DEFECTO); vector_destruir(v_def); return 0; }
Ejercicio 2.06.11 - Reducción Dinámica de Capacidad (Shrinking) ⭐⭐⭐☆☆¶
Modificar el vector del ejercicio anterior para reducir su capacidad a la mitad si la cantidad de elementos en uso cae por debajo de 1/4 de su capacidad máxima.
Ejercicio 2.06.12 - Gestión de Memoria en el Parser JSON ⭐⭐☆☆☆¶
Diseñar las funciones de reserva y liberación para un nodo AST de un parser JSON que representa objetos y arreglos anidados mediante punteros dinámicos.
Ejercicio 2.06.13 - Heap Buffer Overflow ⭐⭐☆☆☆¶
Escribir un fragmento de código que produzca un desbordamiento de búfer en el Heap y explicar cómo AddressSanitizer reporta dicho error.
Ejercicio 2.06.14 - Matriz Dinámica Dentada ⭐⭐☆☆☆¶
Creá matriz donde cada fila tiene diferente cantidad de columnas.
Orientación:
1 2 3 4 5 6 7int filas = 3; int cols[] = {2, 4, 3}; int **matriz = malloc(filas * sizeof(int *)); for (int i = 0; i < filas; i++) { matriz[i] = malloc(cols[i] * sizeof(int)); }
Liberación: cada fila primero, luego array de punteros
Ejercicio 2.06.15 - Matriz Dinámica en Bloque ⭐⭐⭐☆☆¶
Creá matriz contigua en memoria (un solo malloc para datos).
Orientación:
1 2 3 4 5 6 7 8 9 10int **crear_matriz(int filas, int cols) { int **matriz = malloc(filas * sizeof(int *)); int *datos = malloc(filas * cols * sizeof(int)); for (int i = 0; i < filas; i++) { matriz[i] = datos + i * cols; } return matriz; }
Ventaja: mejor localidad de caché
Liberación: liberar datos, luego array de punteros
Ejercicio 2.06.16 - Matriz con Cast (ALV) ⭐⭐⭐☆☆¶
Implementá acceso a matriz unidimensional como bidimensional.
Orientación:
int *matriz = malloc(filas * cols * sizeof(int));
// Acceso: matriz[i * cols + j]
// O macro:
#define MAT(m, i, j, cols) ((m)[(i) * (cols) + (j)])
MAT(matriz, 2, 3, cols) = 42;Un solo
mallocyfreeMenos flexible pero más eficiente
Ejercicio 2.06.17 - Redimensionar Array Dinámico ⭐⭐⭐☆☆¶
Implementá función para redimensionar array preservando datos.
Orientación:
1 2 3 4 5 6 7 8 9 10 11int *redimensionar(int *arr, int tam_actual, int tam_nuevo) { int *nuevo = realloc(arr, tam_nuevo * sizeof(int)); if (nuevo == NULL) { // Manejar error, NO liberar arr return NULL; } // Si tam_nuevo > tam_actual, nuevos elementos sin inicializar return nuevo; }
Ejercicio 2.06.18 - Array de Strings Dinámico ⭐⭐⭐☆☆¶
Creá array dinámico de strings donde cada string también es dinámico.
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12char **strings = malloc(n * sizeof(char *)); for (int i = 0; i < n; i++) { strings[i] = malloc((strlen(input) + 1) * sizeof(char)); strcpy(strings[i], input); } // Liberación: for (int i = 0; i < n; i++) { free(strings[i]); } free(strings);
Ejercicio 2.06.19 - Estructura con Arrays Dinámicos ⭐⭐⭐☆☆¶
Creá estructura que contenga arrays dinámicos.
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19typedef struct { int *datos; size_t tamanio; size_t capacidad; } vector_t; vector_t *crear_vector(size_t cap_inicial) { vector_t *v = malloc(sizeof(vector_t)); v->datos = malloc(cap_inicial * sizeof(int)); v->tamanio = 0; v->capacidad = cap_inicial; return v; } void destruir_vector(vector_t *v) { free(v->datos); // Primero datos free(v); // Luego estructura }
Ejercicio 2.06.20 - Lista Enlazada con Strings ⭐⭐⭐⭐☆¶
Implementá lista donde cada nodo contiene un string dinámico.
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18typedef struct nodo { char *str; // String dinámico struct nodo *siguiente; } nodo_t; nodo_t *crear_nodo(const char *str) { nodo_t *nuevo = malloc(sizeof(nodo_t)); nuevo->str = malloc(strlen(str) + 1); strcpy(nuevo->str, str); nuevo->siguiente = NULL; return nuevo; } void liberar_nodo(nodo_t *nodo) { free(nodo->str); // Primero el string free(nodo); // Luego el nodo }
Ejercicio 2.06.21 - Estructura Jerárquica de Categorías con Memoria Dinámica ⭐⭐⭐⭐⭐¶
Implementá una estructura de categorías donde cada categoría contiene un arreglo dinámico de subcategorías y un nombre dinámico:
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30typedef struct categoria { char *nombre; struct categoria **hijos; size_t num_hijos; } categoria_t; categoria_t *crear_categoria(const char *nombre) { categoria_t *c = malloc(sizeof(categoria_t)); c->nombre = strdup(nombre); c->hijos = NULL; c->num_hijos = 0; return c; } void liberar_categoria(categoria_t *c) { if (c == NULL) { return; } for (size_t i = 0; i < c->num_hijos; i++) { liberar_categoria(c->hijos[i]); } free(c->hijos); free(c->nombre); free(c); }
Ejercicio 2.06.22 - Matriz Triangular ⭐⭐⭐⭐☆¶
Implementá matriz triangular inferior (solo almacená elementos <= diagonal).
Orientación:
Fila i tiene i+1 elementos
Total elementos: n(n+1)/2
int **matriz = malloc(n * sizeof(int *));
for (int i = 0; i < n; i++)
{
matriz[i] = malloc((i + 1) * sizeof(int));
}Ejercicio 2.06.23 - Copiar Estructura Profunda ⭐⭐⭐⭐☆¶
Implementá copia profunda de estructura con punteros.
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16typedef struct { char *nombre; int *calificaciones; size_t num_calificaciones; } estudiante_t; estudiante_t *copiar(const estudiante_t *orig) { estudiante_t *copia = malloc(sizeof(estudiante_t)); copia->nombre = strdup(orig->nombre); // O malloc+strcpy copia->num_calificaciones = orig->num_calificaciones; copia->calificaciones = malloc(orig->num_calificaciones * sizeof(int)); memcpy(copia->calificaciones, orig->calificaciones, orig->num_calificaciones * sizeof(int)); return copia; }
Ejercicio 2.06.24 - Matriz Dinámica de Conectividad NxN ⭐⭐⭐⭐⭐¶
Creá una matriz bidimensional dinámica booleana/entera para registrar conexiones entre elementos.
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27typedef struct { int **matriz; // Matriz NxN int dimension; } red_conexiones_t; red_conexiones_t *crear_red(int n) { red_conexiones_t *r = malloc(sizeof(red_conexiones_t)); r->dimension = n; r->matriz = malloc(n * sizeof(int *)); for (int i = 0; i < n; i++) { r->matriz[i] = calloc(n, sizeof(int)); // Inicializado a 0 } return r; } void liberar_red(red_conexiones_t *r) { for (int i = 0; i < r->dimension; i++) { free(r->matriz[i]); } free(r->matriz); free(r); }
Ejercicio 2.06.25 - Array de Estructuras con Punteros ⭐⭐⭐⭐☆¶
Creá array dinámico de estructuras que contienen punteros.
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14typedef struct { char *titulo; int *capitulos; int num_caps; } libro_t; libro_t *libros = malloc(n * sizeof(libro_t)); // Liberación compleja: cada campo de cada estructura for (int i = 0; i < n; i++) { free(libros[i].titulo); free(libros[i].capitulos); } free(libros);
Ejercicio 2.06.26 - Tabla de Registros con Redimensionamiento Dinámico ⭐⭐⭐⭐⭐¶
Implementá un contenedor indexado de punteros a estructuras con redimensionamiento dinámico cuando la capacidad se agote.
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23typedef struct { char *clave; int valor; } registro_t; typedef struct { registro_t **items; size_t capacidad; size_t num_elementos; } tabla_registros_t; void redimensionar_tabla(tabla_registros_t *t) { size_t nueva_cap = t->capacidad * 2; registro_t **nuevos = realloc(t->items, nueva_cap * sizeof(registro_t *)); if (nuevos != NULL) { t->items = nuevos; t->capacidad = nueva_cap; } }
Ejercicio 2.06.27 - Matriz Dispersa (Sparse Matrix) ⭐⭐⭐⭐⭐¶
Implementá matriz dispersa con lista de triplas (fila, col, valor).
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12typedef struct { int fila, col; double valor; } tripla_t; typedef struct { tripla_t *elementos; size_t num_elementos; size_t capacidad; int filas, cols; } matriz_dispersa_t;
Solo almacená elementos != 0
Búsqueda lineal o binaria para acceso
Ejercicio 2.06.28 - Buffer Circular Dinámico ⭐⭐⭐⭐⭐¶
Implementá buffer circular con redimensionamiento.
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24typedef struct { void **datos; size_t capacidad; size_t inicio, fin; size_t tamanio; } buffer_circular_t; void redimensionar_buffer(buffer_circular_t *b) { size_t nueva_cap = b->capacidad * 2; void **nuevo = malloc(nueva_cap * sizeof(void *)); // Copiar elementos en orden size_t idx = b->inicio; for (size_t i = 0; i < b->tamanio; i++) { nuevo[i] = b->datos[idx]; idx = (idx + 1) % b->capacidad; } free(b->datos); b->datos = nuevo; b->inicio = 0; b->fin = b->tamanio; b->capacidad = nueva_cap; }
Ejercicio 2.06.29 - Punteros a Punteros para Modificar ⭐⭐⭐⭐☆¶
Implementá función que modifica puntero pasado como argumento.
Orientación:
1 2 3 4 5 6 7 8 9 10void insertar_inicio(nodo_t **cabeza, int valor) { nodo_t *nuevo = malloc(sizeof(nodo_t)); nuevo->dato = valor; nuevo->siguiente = *cabeza; *cabeza = nuevo; // Modifica el puntero original } // Uso: nodo_t *lista = NULL; insertar_inicio(&lista, 42); // Pasa dirección del puntero
Ejercicio 2.06.30 - Array 3D Dinámico ⭐⭐⭐⭐⭐¶
Creá array tridimensional dinámico.
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25int ***crear_array_3d(int x, int y, int z) { int ***arr = malloc(x * sizeof(int **)); for (int i = 0; i < x; i++) { arr[i] = malloc(y * sizeof(int *)); for (int j = 0; j < y; j++) { arr[i][j] = malloc(z * sizeof(int)); } } return arr; } void liberar_array_3d(int ***arr, int x, int y) { for (int i = 0; i < x; i++) { for (int j = 0; j < y; j++) { free(arr[i][j]); // Nivel más profundo primero } free(arr[i]); } free(arr); }
Ejercicio 2.06.31 - Pool de Objetos ⭐⭐⭐⭐⭐¶
Implementá pool de objetos para evitar malloc/free frecuentes.
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18typedef struct { void *bloques; size_t tam_objeto; size_t capacidad; void **libres; // Stack de objetos libres size_t num_libres; } pool_t; void *pool_alloc(pool_t *p) { if (p->num_libres == 0) return NULL; return p->libres[--p->num_libres]; } void pool_free(pool_t *p, void *obj) { p->libres[p->num_libres++] = obj; }
Ejercicio 2.06.32 - Reference Counting ⭐⭐⭐⭐⭐¶
Implementá sistema de conteo de referencias para compartir datos.
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24typedef struct { void *datos; size_t contador_refs; } ref_counted_t; ref_counted_t *crear_ref(void *datos) { ref_counted_t *r = malloc(sizeof(ref_counted_t)); r->datos = datos; r->contador_refs = 1; return r; } void incrementar_ref(ref_counted_t *r) { r->contador_refs++; } void decrementar_ref(ref_counted_t *r, void (*destruir)(void *)) { if (--r->contador_refs == 0) { destruir(r->datos); free(r); } }
Ejercicio 2.06.33 - Sistema de Memoria con Debug ⭐⭐⭐⭐⭐¶
Implementá wrapper de malloc/free que registre asignaciones.
Orientación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19typedef struct { void *ptr; size_t tamanio; const char *archivo; int linea; } alloc_info_t; #define MALLOC_DEBUG(size) malloc_debug(size, __FILE__, __LINE__) #define FREE_DEBUG(ptr) free_debug(ptr, __FILE__, __LINE__) void *malloc_debug(size_t size, const char *file, int line) { void *ptr = malloc(size); // Registrar en tabla de asignaciones return ptr; } void mostrar_leaks() { // Mostrar asignaciones no liberadas }
Notas Finales¶
Estas consignas cubren gestión avanzada de memoria y estructuras dinámicas complejas, esenciales para implementar TADs y aplicaciones de performance crítica.