Acerca de¶
A diferencia de otros ejercicios, el objetivo aquí no es escribir código, sino analizarlo. La capacidad de leer, entender y razonar sobre un código existente es una habilidad tan importante como la de escribirlo. Estos ejercicios están diseñados para aplicar los conceptos teóricos de los apuntes sobre Roles de variables y Estado de un programa.
1: Identificación de Roles de Variables¶
1.1: Análisis de una Función¶
Solution to Exercise 1
suma
: Acumulador. Su propósito es acumular la suma de los valores positivos encontrados.contador
: Contador. Su rol es contar cuántos números positivos se han encontrado.exito
: Bandera (Flag). Se utiliza para señalizar al código que llama a la función si la operación fue exitosa (es decir, si se encontró al menos un número positivo).i
: Variable de Control de Bucle (Iterador). Su único propósito es controlar las iteraciones del buclefor
.Valor de retorno: Variable de Salida. Contiene el resultado principal del cálculo de la función.
1.2: Análisis de Función de Búsqueda¶
Solution to Exercise 2
maximo
: Variable de Mejor Candidato/Guardián. Mantiene el valor máximo encontrado hasta el momento.hay_elementos
: Bandera de Inicialización. Indica si ya se procesó al menos un elemento para inicializar correctamente la comparación.encontrado
: Parámetro de Salida/Bandera de Estado. Comunica al llamador si la operación fue exitosa.indice
: Variable de Control de Bucle/Iterador. Controla el recorrido del arreglo.
1.3: Análisis de Función con Múltiples Roles¶
Solution to Exercise 3
resultado
: Variable de Salida Estructurada. Encapsula múltiples valores de retorno en una estructura.acumulado
: Acumulador. Suma todas las ventas válidas encontradas.dias_activos
: Contador. Cuenta los días que tuvieron ventas positivas.primera_venta
: Bandera de Primera Vez. Controla la inicialización correcta del máximo.venta_maxima
: Variable de Mejor Candidato/Guardián. Mantiene el valor de venta más alto encontrado.dia
: Variable de Control de Bucle/Iterador. Controla la iteración a través de los días.
2: Descripción del Estado de un Programa¶
2.1: Fotografía de la Memoria¶
Solution to Exercise 4
Pila (Stack):
Marco de
main
:saludo_original
(puntero): Contiene la dirección de memoria de la cadena literal “Hola”.saludo_copiado
(puntero): Su valor esNULL
en este punto, ya que la asignación se produce después de quecrear_copia
retorne.
Marco de
crear_copia
:original
(puntero): Contiene una copia de la dirección desaludo_original
, por lo que también apunta a la cadena literal “Hola”.largo
(entero): Su valor es4
(calculado por el buclewhile
).copia
(puntero): Contiene la dirección de memoria del nuevo bloque de 5 bytes reservado pormalloc
en el montículo.i
(entero): Aún no ha sido inicializada, por lo que su valor es indeterminado (basura).
Montículo (Heap):
Hay un bloque de 5 bytes reservado. Su contenido es indeterminado (basura), ya que el bucle
for
que lo llena aún no se ha ejecutado.
Segmento de Datos (Solo Lectura):
Contiene la cadena literal
"Hola"
(terminada en nulo), que ocupa 5 bytes. La dirección de su primer carácter es a la que apuntansaludo_original
yoriginal
.
2.2: Análisis de Memoria con Estructuras¶
Solution to Exercise 5
Pila (Stack):
Marco de
main
:emp1
(puntero):NULL
(aún no asignado, la función no ha retornado)emp2
(puntero):NULL
Marco de
crear_empleado
:nom
(puntero): Apunta a la cadena literal “Ana García” en el segmento de datosedad_emp
(int): 28sal
(double): 45000.0nuevo
(puntero): Apunta al bloque desizeof(empleado_t)
bytes en el heaplen_nombre
(int): 10 (longitud de “Ana García”)
Montículo (Heap):
Bloque 1:
sizeof(empleado_t)
bytes para la estructura empleadonombre
(puntero): Apunta al segundo bloque en el heapedad
(int): 28salario
(double): 45000.0
Bloque 2: 11 bytes conteniendo “Ana García\0”
Segmento de Datos (Solo Lectura):
Cadena literal “Ana García” (11 bytes incluyendo ‘\0’)
2.3: Trazado de Ejecución con Arrays Dinámicos¶
Solution to Exercise 6
PUNTO A (después del malloc en duplicar_array
):
Pila:
Marco de
main
:numeros[3]
: {5, 10, 15}duplicados
: NULL
Marco de
duplicar_array
:original
: apunta anumeros[0]
en maintam
: 3nuevo
: apunta al bloque malloc’eado en heapi
: no inicializada (basura)
Heap: Bloque de 12 bytes (3 * sizeof(int)) con contenido indeterminado
PUNTO B (después del bucle for):
Pila: Igual que punto A, excepto
i
= 3Heap: El bloque ahora contiene {10, 20, 30}
PUNTO C (antes de llamar a duplicar_array
):
Pila:
Marco de
main
:numeros[3]
: {5, 10, 15}duplicados
: NULL
Heap: Vacío
PUNTO D (después de retornar de duplicar_array
):
Pila:
Marco de
main
:numeros[3]
: {5, 10, 15}duplicados
: apunta al bloque en heap
Heap: Bloque de 12 bytes conteniendo {10, 20, 30}
3: Análisis de Bugs y Problemas¶
3.1: Identificación de Errores de Lógica¶
Solution to Exercise 7
Problemas identificados:
Falta validación de tamaño: No verifica que
tam >= 2
, que es el mínimo necesario para tener un segundo máximo.Elementos duplicados: Si el máximo se repite, el segundo máximo será igual al máximo, lo cual puede no ser el comportamiento deseado.
Array con menos de 2 elementos únicos: Si todos los elementos son iguales, no hay un verdadero “segundo máximo”.
No maneja el caso de array vacío: Si
tam == 0
, el comportamiento es indefinido.
Casos que fallan:
Array vacío:
[]
Array con un elemento:
[5]
Array con elementos iguales:
[3, 3, 3]
Array con solo dos valores únicos donde uno se repite:
[5, 3, 5, 5]
Corrección sugerida:
int segundo_maximo_corregido(int arr[], int tam) {
if (tam < 2) {
return INT_MIN; // o manejar error apropiadamente
}
int maximo = INT_MIN;
int segundo = INT_MIN;
for (int i = 0; i < tam; i++) {
if (arr[i] > maximo) {
segundo = maximo;
maximo = arr[i];
} else if (arr[i] > segundo && arr[i] != maximo) {
segundo = arr[i];
}
}
// Verificar si realmente hay un segundo máximo
if (segundo == INT_MIN) {
// Todos los elementos eran iguales
return INT_MIN; // o manejar apropiadamente
}
return segundo;
}
3.2: Análisis de Memory Leaks¶
Solution to Exercise 8
Problemas identificados:
Memory leak de
temp
: La variabletemp
se aloca conmalloc()
pero nunca se libera confree()
. Esto ocurre en ambos caminos de ejecución.Memory leak de
resultado
: Cuandolen > 10
, se alocaresultado
pero luego se alocaresultado_largo
y se retorna este último, perdiendo la referencia aresultado
sin liberarlo.Memory leaks en
main
: Las variablestexto1
ytexto2
reciben memoria alocada dinámicamente pero nunca se libera.Uso innecesario de memoria: El
temp
es redundante ya que se puede trabajar directamente conentrada
.
Código corregido:
char* procesar_texto_corregido(const char* entrada) {
int len = strlen(entrada);
char* resultado;
if (len > 10) {
resultado = malloc(len * 3 + 1);
for (int i = 0; i < len; i++) {
resultado[i * 3] = entrada[i];
resultado[i * 3 + 1] = entrada[i];
resultado[i * 3 + 2] = entrada[i];
}
resultado[len * 3] = '\0';
} else {
resultado = malloc(len * 2 + 1);
for (int i = 0; i < len; i++) {
resultado[i * 2] = entrada[i];
resultado[i * 2 + 1] = entrada[i];
}
resultado[len * 2] = '\0';
}
return resultado;
}
int main() {
char* texto1 = procesar_texto_corregido("Hola");
char* texto2 = procesar_texto_corregido("Este es un texto muy largo");
printf("Texto 1: %s\n", texto1);
printf("Texto 2: %s\n", texto2);
// Liberar memoria
free(texto1);
free(texto2);
return 0;
}
4: Análisis de Eficiencia y Optimización¶
4.1: Análisis de Complejidad Temporal¶
Solution to Exercise 9
Análisis de buscar_par_suma
:
Complejidad temporal: - hay dos bucles anidados que recorren el array
Variables de control:
i
: Iterador externo (rol: control de bucle principal)j
: Iterador interno (rol: control de bucle secundario, siempre j > i)
Complejidad espacial: - solo usa variables locales
Análisis de buscar_par_suma_optimizado
:
Complejidad temporal: - cada elemento se visita máximo una vez
Variables de control:
izq
: Puntero izquierdo (rol: guardián del límite inferior)der
: Puntero derecho (rol: guardián del límite superior)suma_actual
: Variable temporal (rol: almacén de cálculo intermedio)
Complejidad espacial:
Escenarios apropiados:
Primer algoritmo: Cuando el array NO está ordenado y no podemos/queremos ordenarlo
Segundo algoritmo: Cuando el array YA está ordenado o el costo de ordenarlo es amortizable
Trade-offs:
Si necesitamos ordenar: vs
Si ya está ordenado: vs - clara ventaja para el optimizado
4.2: Análisis de Uso de Memoria¶
Solution to Exercise 10
Versión Recursiva:
Pila: - cada llamada recursiva agrega un marco a la pila
Heap: - no usa memoria dinámica
Variables: Solo el parámetro
n
en cada marco (rol: parámetro de entrada)Riesgo: Stack overflow para valores grandes de n
Versión Iterativa:
Pila: - un solo marco de función
Heap: - no usa memoria dinámica
Variables:
resultado
: Acumulador para el productoi
: Variable de control de bucle
Ventajas: Uso mínimo de memoria, sin riesgo de stack overflow
Versión Memoizada:
Pila: - similar a recursiva en la primera llamada, en llamadas subsecuentes
Heap: - array para almacenar resultados calculados
Variables:
cache
: Array estático global (rol: almacén de resultados)cache_size
: Contador de tamaño actual (rol: guardián de límites)
Trade-off: Usa más memoria pero acelera dramáticamente cálculos repetidos
Comparación de Trade-offs:
Aspecto | Recursiva | Iterativa | Memoizada |
---|---|---|---|
Tiempo (primera llamada) | |||
Tiempo (llamadas repetidas) | |||
Espacio en pila | → | ||
Espacio en heap | |||
Legibilidad | Alta | Media | Baja |
Riesgo de overflow | Alto | Bajo | Medio |
5: Ejercicios de Síntesis¶
5.1: Análisis Integral de Sistema¶
Solution to Exercise 11
1. Identificación de Roles por Función:
inicializar_sistema
:
capacidad_inicial
: Parámetro de entrada (tamaño inicial)sistema
: Variable de salida local (estructura a retornar)
agregar_estudiante
:
sistema
: Parámetro de entrada/salida (modificado por referencia)nombre
,edad
,promedio
: Parámetros de entradanueva_capacidad
: Variable temporal (cálculo de redimensión)nueva_lista
: Variable temporal/auxiliar (para verificación de realloc)
buscar_mejor_estudiante
:
sistema
: Parámetro de entrada (solo lectura)mejor
: Variable de mejor candidato/guardiáni
: Variable de control de bucle
liberar_sistema
:
sistema
: Parámetro de entrada (puntero a liberar)
2. Análisis de Memoria:
Estado inicial:
Heap:
sizeof(sistema_estudiantes_t)
+capacidad_inicial * sizeof(estudiante_t)
Pila: Variables locales de cada función
Durante agregado:
Si
cantidad < capacidad
: Solo se modifica contenidoSi
cantidad >= capacidad
: Realloc duplica el espacio, posible fragmentación
3. Patrones de Diseño:
Dynamic Array: Lista que crece automáticamente
Handle Pattern: El sistema actúa como handle para la colección
RAII-like: Funciones específicas para inicializar y limpiar
4. Análisis de Robustez - Problemas identificados:
Falta de validaciones:
// En agregar_estudiante - faltan validaciones:
if (sistema == NULL || nombre == NULL) {
return false;
}
if (strlen(nombre) >= 50) { // nombre muy largo
return false;
}
if (edad < 0 || edad > 120) { // edad inválida
return false;
}
if (promedio < 0.0 || promedio > 10.0) { // promedio inválido
return false;
}
Problemas de robustez:
No validación de punteros NULL
No verificación de límites de strings
No validación de rangos de datos
Posible integer overflow en
nueva_capacidad
No hay función para verificar estado del sistema
Mejoras sugeridas:
Agregar validaciones de entrada en todas las funciones
Implementar códigos de error más específicos
Agregar función de consulta de estado
Considerar límite máximo de capacidad
Agregar tests unitarios para cada función
Este ejercicio integra todos los conceptos de análisis de código, roles de variables, manejo de memoria y buenas prácticas de programación en C.