Cópia Profunda
Parte da seção Object Oriented Programming do Journey de C da Coddy — lição 15 de 61.
Quando você atribui uma struct a outra em C, você obtém uma cópia rasa (shallow copy)—todos os campos são copiados byte a byte. Isso funciona bem para valores simples como inteiros, mas cria um problema com ponteiros: ambas as structs acabam apontando para a mesma memória.
typedef struct {
char *name;
int age;
} Person;
Person alice = {"Alice", 30};
Person copy = alice; // Cópia rasa!
// Tanto alice.name quanto copy.name apontam para a MESMA string
Se você liberar um, o outro se torna inválido. Se você modificar a string por meio de um, ambos verão a mudança. Isso raramente é o que você deseja.
Uma cópia profunda (deep copy) aloca nova memória para membros de ponteiro e copia os dados reais:
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;
}
Agora cada objeto possui sua própria string. Você pode modificar ou liberar um sem afetar o outro. O padrão é simples: para cada membro ponteiro, aloque nova memória e copie o conteúdo — não apenas o endereço.
Desafio
FácilVamos construir um módulo Student que demonstra o padrão de cópia profunda (deep copy). Você criará uma função de clonagem que produz uma cópia completamente independente de um objeto de estudante — uma em que a modificação ou a liberação do original não tenha efeito sobre o clone.
Você criará três arquivos:
student.h: Declare uma structStudentcom dois membros:char *name(uma string alocada dinamicamente) eint grade. Declare três funções:create_student— construtor que recebe um nome e uma nota, retornando um ponteiro para um novo Studentclone_student— recebe umconst Student *e retorna um ponteiro para uma nova cópia independentefree_student— destrutor que libera adequadamente toda a memória alocada
STUDENT_H.student.c: Implemente as três funções. Sua funçãoclone_studenté a chave — ela deve alocar memória nova tanto para a struct quanto para a string de nome, e então copiar os dados reais da string (não apenas o endereço do ponteiro). Isso garante que o clone possua sua própria memória, completamente separada do original.main.c: Crie um estudante, clone-o, exiba as informações de ambos os estudantes e, em seguida, limpe adequadamente ambos os objetos.
Você receberá duas entradas: o nome do estudante (uma string) e a nota (um inteiro).
No seu arquivo principal, crie um estudante com os valores fornecidos e, em seguida, crie um clone desse estudante. Imprima as informações do original e do clone para verificar se eles contêm os mesmos dados. Por fim, libere ambos os estudantes e imprima uma confirmação.
Imprima a saída neste formato:
Original: {name}, Grade: {grade}
Clone: {name}, Grade: {grade}
Both freedPor exemplo, com as entradas Alice e 95, a saída seria:
Original: Alice, Grade: 95
Clone: Alice, Grade: 95
Both freedO padrão de cópia profunda garante que cada objeto seja verdadeiramente independente — o clone tem sua própria string alocada, não um ponteiro compartilhado para a memória do original.
Folha de consulta
Quando você atribui uma struct a outra em C, você obtém uma cópia rasa (shallow copy)—todos os campos são copiados byte a byte. Com ponteiros, ambas as structs acabam apontando para a mesma memória:
typedef struct {
char *name;
int age;
} Person;
Person alice = {"Alice", 30};
Person copy = alice; // Cópia rasa!
// Tanto alice.name quanto copy.name apontam para a MESMA string
Uma cópia profunda (deep copy) aloca nova memória para membros de ponteiro e copia os dados reais:
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;
}
O padrão de cópia profunda: para cada membro de ponteiro, aloque memória nova e copie o conteúdo—não apenas o endereço. Isso garante que cada objeto possua sua própria memória e possa ser modificado ou liberado de forma independente.
Experimente você mesmo
#include <stdio.h>
#include <stdlib.h>
#include "student.h"
int main() {
char name[100];
int grade;
// Ler entrada
scanf("%s", name);
scanf("%d", &grade);
// TODO: Criar um estudante com o nome e a nota fornecidos
// TODO: Criar um clone do estudante
// TODO: Imprimir as informações do estudante original
// Formato: "Original: {name}, Grade: {grade}"
// TODO: Imprimir as informações do clone
// Formato: "Clone: {name}, Grade: {grade}"
// TODO: Liberar ambos os estudantes
// Imprimir confirmação
printf("Both freed\n");
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Object Oriented Programming
1Fundamentos da Programação Modular
Arquivos de CabeçalhoInclude GuardsArquivos-FonteFunções EstáticasRecapitulação: Calculadora Modular4Encapsulamento
Conceito de Ponteiros OpacosDefinindo Structs OpacasGetters e SettersValidação em SettersRecapitulação: Caixa Secreta2Objetos e Métodos
Structs como ObjetosO Ponteiro 'Self'Const CorrectnessPonteiro vs ValorMétodos AuxiliaresRecapitulação: Point Manager5Projeto: Conta Bancária Simples
Configuração do ProjetoImplementação da Conta3Ciclo de Vida de Objetos
Padrão de ConstrutorPadrão de DestrutorInicialização na StackCópia ProfundaRecapitulação: String Wrapper6Herança via Composição
Incorporação de StructsA Regra do Primeiro MembroAcessando Membros PaiUpcastingRecapitulação: Hierarquia de Formas9Projeto: Desenhador de Formas
Visão Geral do ProjetoImplementação do Círculo