Copia profunda
Parte de la sección Object Oriented Programming del Journey de C de Coddy — lección 15 de 61.
Cuando asignas un struct a otro en C, obtienes una copia superficial—todos los campos se copian byte por byte. Esto funciona bien para valores simples como enteros, pero crea un problema con los punteros: ambos structs terminan apuntando a la misma memoria.
typedef struct {
char *name;
int age;
} Person;
Person alice = {"Alice", 30};
Person copy = alice; // ¡Copia superficial!
// Tanto alice.name como copy.name apuntan a la MISMA cadena
Si liberas uno, el otro se vuelve inválido. Si modificas la cadena a través de uno, ambos ven el cambio. Esto es raramente lo que quieres.
Una copia profunda asigna nueva memoria para los miembros de tipo puntero y copia los datos reales:
Person *clone_person(const Person *original) {
Person *copy = malloc(sizeof(Person));
copy->name = malloc(strlen(original->name) + 1);
strcpy(copy->name, original->name);
copy->age = original->age;
return copy;
}
Ahora cada objeto posee su propia cadena. Puedes modificar o liberar una sin afectar a la otra. El patrón es sencillo: para cada miembro de tipo puntero, asigna memoria nueva y copia el contenido, no solo la dirección.
Desafío
FácilVamos a construir un módulo Student que demuestre el patrón de copia profunda (deep copy). Crearás una función de clonación que produzca una copia completamente independiente de un objeto estudiante, una en la que modificar o liberar el original no tenga efecto sobre el clon.
Crearás tres archivos:
student.h: Declara una estructuraStudentcon dos miembros:char *name(una cadena asignada dinámicamente) eint grade. Declara tres funciones:create_student— constructor que recibe un nombre y una calificación, y devuelve un puntero a un nuevo Studentclone_student— recibe unconst Student *y devuelve un puntero a una copia nueva e independientefree_student— destructor que libera adecuadamente toda la memoria asignada
STUDENT_H.student.c: Implementa las tres funciones. Tu funciónclone_studentes la clave: debe asignar memoria nueva tanto para la estructura como para la cadena de nombre, y luego copiar los datos reales de la cadena (no solo la dirección del puntero). Esto asegura que el clon posea su propia memoria de forma completamente separada del original.main.c: Crea un estudiante, clónalo, muestra la información de ambos estudiantes y luego limpia adecuadamente ambos objetos.
Recibirás dos entradas: el nombre del estudiante (una cadena) y la calificación (un entero).
En tu archivo principal, crea un estudiante con los valores proporcionados, luego crea un clon de ese estudiante. Imprime la información tanto del original como del clon para verificar que contienen los mismos datos. Finalmente, libera ambos estudiantes e imprime una confirmación.
Imprime la salida en este formato:
Original: {name}, Grade: {grade}
Clone: {name}, Grade: {grade}
Both freedPor ejemplo, con las entradas Alice y 95, la salida sería:
Original: Alice, Grade: 95
Clone: Alice, Grade: 95
Both freedEl patrón de copia profunda asegura que cada objeto sea verdaderamente independiente: el clon tiene su propia cadena asignada, no un puntero compartido a la memoria del original.
Hoja de referencia
Cuando asignas una estructura a otra en C, obtienes una copia superficial (shallow copy); todos los campos se copian byte por byte. Con los punteros, ambas estructuras terminan apuntando a la misma memoria:
typedef struct {
char *name;
int age;
} Person;
Person alice = {"Alice", 30};
Person copy = alice; // ¡Copia superficial!
// Tanto alice.name como copy.name apuntan a la MISMA cadena
Una copia profunda (deep copy) asigna nueva memoria para los miembros que son punteros y copia los datos reales:
Person *clone_person(const Person *original) {
Person *copy = malloc(sizeof(Person));
copy->name = malloc(strlen(original->name) + 1);
strcpy(copy->name, original->name);
copy->age = original->age;
return copy;
}
El patrón de copia profunda: para cada miembro puntero, asigna memoria nueva y copia el contenido, no solo la dirección. Esto asegura que cada objeto sea dueño de su propia memoria y pueda ser modificado o liberado de forma independiente.
Pruébalo tú mismo
#include <stdio.h>
#include <stdlib.h>
#include "student.h"
int main() {
char name[100];
int grade;
// Leer entrada
scanf("%s", name);
scanf("%d", &grade);
// TODO: Crear un estudiante con el nombre y la calificación proporcionados
// TODO: Crear un clon del estudiante
// TODO: Imprimir la información del estudiante original
// Formato: "Original: {name}, Grade: {grade}"
// TODO: Imprimir la información del clon
// Formato: "Clone: {name}, Grade: {grade}"
// TODO: Liberar a ambos estudiantes
// Imprimir confirmación
printf("Both freed\n");
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Fundamentos de programación modular
Archivos de cabeceraGuardas de inclusiónArchivos fuenteFunciones estáticasRepaso: Calculadora modular4Encapsulamiento
Concepto de punteros opacosDefinición de structs opacosGetters y SettersValidación en settersResumen: Caja secreta7Punteros a funciones
Declaración de punteros a funcionesLlamada a punteros a funcionesTypedef para punteros a funcionesPasar funciones como argumentosRepaso: Dispatch de calculadora10Contenedores genéricos
Repaso de punteros voidWrapper genéricoSwap genéricoComparación genéricaRepaso: Array genérico2Objetos y métodos
Structs como objetosEl puntero 'Self'Const CorrectnessPuntero vs. ValorMétodos auxiliaresResumen: Point Manager5Proyecto: Cuenta bancaria simple
Configuración del proyectoImplementación de la cuenta3Ciclo de vida de los objetos
Patrón ConstructorPatrón DestructorInicialización en StackCopia profundaResumen: String Wrapper6Herencia mediante composición
Incrustación de StructsLa regla del primer miembroAcceso a miembros padreUpcastingResumen: Jerarquía de formas9Proyecto: Dibujador de figuras
Descripción general del proyectoImplementación del círculo