Mejoras y correcciones en proyecto de gestión estudiantil en C

Este proyecto corresponde a un trabajo académico de primer semestre de carrera universitaria. Después de un año adicional de experiencia en programación, he vuelto a revisar el código original y he identificado varias áreas que necesitan optimización.

El enunciado original establecía los siguientes requisitos:

1. Implementar búsqueda, adición, eliminación, modificación, visualización y almacenamiento de información de estudiantes, además de carga desde archivo y consulta de becas. Cada módulo debe permitir salida anticipada y ofrecer múltiples formas de ejecutar las funciones.
2. Utilizar estructuras para almacenar la información de los estudiantes.
3. Emplear listas enlazadas o arreglos para las operaciones de gestión de datos.
4. Manejar archivos para persistencia, cargando datos al iniciar cada módulo y ofreciendo opciones de guardado.

La definición de estructura utilizada originalmente era:

Ver código``` typedef struct Estudiante { int codigo; char nombre[50]; float puntuacion; char categoriaBeca[50]; struct Estudiante *siguiente; } Estudiante;


Para efectos de esta evaluación, se establecieron las siguientes restricciones en los datos de entrada:

| Campo | Restricción |
|-------|-------------|
| codigo | Entero de 3 dígitos sin iniciar en cero |
| nombre[50] | Caracteres chinos sin espacios |
| puntuacion | Entero entre 0 y 100 |
| categoriaBeca[50] | Uno de: "primera", "segunda", "tercera" o "ninguna" |

Para optimizar el proceso de pruebas, el menú principal se mostrará completo solo en la primera iteración.

El primer aspecto a corregir es la función de alta de estudiantes. El código original carece de validación para evitar identificadores duplicados:

Ver código```
void registrarEstudiante() {
    Estudiante *nuevo = (Estudiante *)malloc(sizeof(Estudiante));

    printf("Ingrese codigo de estudiante: ");
    scanf("%d", &nuevo->codigo);
    printf("Ingrese nombre: ");
    scanf("%s", nuevo->nombre);
    printf("Ingrese puntuacion: ");
    scanf("%f", &nuevo->puntuacion);
    printf("Ingrese categoria de beca: ");
    scanf("%s", nuevo->categoriaBeca);

    nuevo->siguiente = NULL;

    if (cabeza == NULL) {
        cabeza = nuevo;
    } else {
        Estudiante *temp = cabeza;
        while (temp->siguiente != NULL) {
            temp = temp->siguiente;
        }
        temp->siguiente = nuevo;
    }
}

La ausencia de verificación permite registrar múltiples estudiantes con el mismo código, generando inconsistencias:

Ver código```


Sistema de Gestion Estudiantil

  1. Agregar estudiante
  2. Eliminar estudiante
  3. Modificar datos
  4. Listar estudiantes
  5. Guardar datos
  6. Cargar desde archivo
  7. Ver informacion de becas
  8. Registro masivo
  9. Salir

Seleccione operacion: 1 Ingrese codigo: 101 Ingrese nombre: Silva Ingrese puntuacion: 90 Ingrese categoria: primera


Seleccione operacion: 4 Codigo: 101, Nombre: Silva, Puntuacion: 90.00, Beca: primera


Seleccione operacion: 5 Datos guardados exitosamente


Seleccione operacion: 6 Datos cargados correctamente


Seleccione operacion: 4 Codigo: 101, Nombre: Silva, Puntuacion: 90.00, Beca: primera Codigo: 101, Nombre: Silva, Puntuacion: 90.00, Beca: primera


Se implementó una rutina de validación inmediata después de capturar el código del estudiante. El fragmento añadido verifica la existencia previa:

Ver código```
Estudiante *recorrido = cabeza;
int duplicado = 0; 
while (recorrido != NULL) {
    if (recorrido->codigo == nuevo->codigo) {
        duplicado = 1; 
        break;
    }
    recorrido = recorrido->siguiente;
}
if (duplicado) {
    printf("Advertencia: El codigo ya esta registrado\n");
    free(nuevo);
    return; 
}

Con esta modificación, al intentar registrar un código existente se muestra el mensaje correspondiente:

Ver código``` Seleccione operacion: 1 Ingrese codigo: 101 Advertencia: El codigo ya esta registrado


El segundo problema identificado se encuentra en la función de carga de información desde archivo:

Ver código```
void importarDatos() {
    FILE *archivo = fopen("estudiantes.dat", "rb");
    if (archivo == NULL) {
        printf("No se pudo abrir el archivo\n");
        return;
    }
    Estudiante *nodo;
    while (1) {
        nodo = (Estudiante *)malloc(sizeof(Estudiante));
        if (fread(nodo, sizeof(Estudiante), 1, archivo) == 0) {
            free(nodo);
            break;
        }
        nodo->siguiente = NULL;
        insertarAlFinal(nodo); 
    }

    fclose(archivo);
    printf("Informacion cargada correctamente\n");
}

El código actual concatena los datos del archivo a la lista existente en memoria, causando dupliacción cuando se ejecuta la carga múltiples veces:

Ver código```


Sistema de Gestion Estudiantil

  1. Agregar estudiante
  2. Eliminar estudiante
  3. Modificar datos
  4. Listar estudiantes
  5. Guardar datos
  6. Cargar desde archivo
  7. Ver informacion de becas
  8. Registro masivo
  9. Salir

Seleccione operacion: 1 Ingrese codigo: 101 Ingrese nombre: Silva Ingrese puntuacion: 90 Ingrese categoria: primera


Seleccione operacion: 4 Codigo: 101, Nombre: Silva, Puntuacion: 90.00, Beca: primera


Seleccione operacion: 5 Datos guardados exitosamente


Seleccione operacion: 6 Datos cargados correctamente


Seleccione operacion: 4 Codigo: 101, Nombre: Silva, Puntuacion: 90.00, Beca: primera Codigo: 101, Nombre: Silva, Puntuacion: 90.00, Beca: primera


Se agregó una función para liberar la memoria antes de cargar nuevos datos:

Ver código```
void limpiarLista() {
    Estudiante *actual = cabeza;
    while (actual != NULL) {
        Estudiante *proximo = actual->siguiente; 
        free(actual); 
        actual = proximo; 
    }
    cabeza = NULL;
}

La llamada a esta función se inserta al inicio del proceso de carga, justo después de verificar que el archivo pudo abrirse correctamente.

El resultado de las pruebas posteriores muestra que la duplicación ha sido eliminada:

Ver código```


Sistema de Gestion Estudiantil

  1. Agregar estudiante
  2. Eliminar estudiante
  3. Modificar datos
  4. Listar estudiantes
  5. Guardar datos
  6. Cargar desde archivo
  7. Ver informacion de becas
  8. Registro masivo
  9. Salir

Seleccione operacion: 1 Ingrese codigo: 101 Ingrese nombre: Silva Ingrese puntuacion: 90 Ingrese categoria: primera


Seleccione operacion: 4 Codigo: 101, Nombre: Silva, Puntuacion: 90.00, Beca: primera


Seleccione operacion: 5 Datos guardados exitosamente


Seleccione operacion: 6 Datos cargados correctamente


Seleccione operacion: 4 Codigo: 101, Nombre: Silva, Puntuacion: 90.00, Beca: primera


Durante el proceso de refactorización surgieron algunos desafíos técnicos. El primer obstáculo fue la diferencia en cnovenciones de indentación: mientras el código original utilizaba el formato `if(){...}`, mi preferencia personal es `if()\n{...}`. El segundo desafío involucró las convenciones de nomenclatura: personalmente prefiero usar guiones bajos para separar palabras (como `insertar_al_final`), mientras que el autor original usaba notación camelCase.

Ambos factores impactan significativamente la legibilidad del código y la mantenibilidad futura. Por esta razón, decidí mantener el estilo original del proyecto para preservar la consistencia. Adicionalmente, aunque el código contaba con comentarios detallados, las operaciones con listas enlazadas seguían un patrón distinto al que estoy acostumbrado, requiriendo un análisis adicional para comprender completamente la lógica de inserción y eliminación de nodos.

Etiquetas: estructuras-datos C listas-enlazadas gestion-memoria programacion-academica

Publicado el 6-30 01:08