Appearance
[中文版本](/posts/c教程/zh-CN/03-内存/029-Deep vs Shallow Copies)
When you assign one variable to another in C, you’re often copying addresses, not actual data. This distinction between shallow copies and deep copies becomes critical when working with pointers, arrays, and dynamically allocated structures. Understanding it helps you prevent memory corruption, double frees, and mysterious bugs.
The Core Idea
- A shallow copy duplicates only the pointer, both variables refer to the same memory.
- A deep copy duplicates the data itself, each variable owns its own independent memory.
Simple Analogy
Think of shallow vs deep copy like two houses:
- Shallow copy: You hand someone your house key. You both open the same door.
- Deep copy: You build a new house that looks identical, but is separate.
Shallow Copy Example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char *original = malloc(10);
strcpy(original, "Hello");
// Shallow copy
char *copy = original;
printf("Before change: %s | %s\n", original, copy);
copy[0] = 'J'; // modify one
printf("After change: %s | %s\n", original, copy);
free(original);
// free(copy); // ❌ would cause double free error!
return 0;
}Output:
Before change: Hello | Hello
After change: Jello | JelloExplanation:
copypoints to the same memory asoriginal.- Changing one changes both.
- You must only
free()it once, freeing both is a bug.
Deep Copy Example
A deep copy allocates new memory and copies the data over.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char *original = malloc(10);
strcpy(original, "Hello");
// Deep copy
char *copy = malloc(strlen(original) + 1);
strcpy(copy, original);
printf("Before change: %s | %s\n", original, copy);
copy[0] = 'J';
printf("After change: %s | %s\n", original, copy);
free(original);
free(copy); // ✅ both safely freed
return 0;
}Output:
Before change: Hello | Hello
After change: Hello | JelloNow the two strings are completely independent, a true deep copy.
Shallow vs Deep in Structs
Consider this structure:
typedef struct {
char *name;
} Person;If you assign onePerson to another:
Person a, b;
a.name = malloc(20);
strcpy(a.name, "Alice");
b = a; // shallow copy
b.name[0] = 'M'; // modifies a.name too!Botha andb point to the same memory. To make a deep copy:
b.name = malloc(strlen(a.name) + 1);
strcpy(b.name, a.name);Now they’re independent.
Tiny Code
Here’s a full program demonstrating both copies with structs:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *name;
int age;
} Person;
void print_person(const char *label, Person p) {
printf("%s: name=%s age=%d\n", label, p.name, p.age);
}
int main(void) {
Person p1;
p1.name = malloc(20);
strcpy(p1.name, "Alice");
p1.age = 25;
// Shallow copy
Person p2 = p1;
print_person("Before", p1);
p2.name[0] = 'M'; // modifies same memory
print_person("After shallow copy", p1);
// Deep copy
Person p3;
p3.name = malloc(strlen(p1.name) + 1);
strcpy(p3.name, p1.name);
p3.age = p1.age;
p3.name[0] = 'C'; // independent copy
print_person("After deep copy", p1);
print_person("Deep copy result", p3);
free(p1.name);
free(p3.name); // ✅ safe
return 0;
}Output:
Before: name=Alice age=25
After shallow copy: name=Mlice age=25
After deep copy: name=Mlice age=25
Deep copy result: name=Clice age=25Why It Matters
Shallow and deep copies determine ownership of memory:
- If two variables share the same pointer (shallow), freeing one invalidates the other.
- Deep copies isolate data, preventing interference but using more memory.
Getting this wrong leads to:
- Double free or dangling pointer errors
- Memory leaks
- Corrupted data in complex structures
Understanding these concepts is crucial for:
- Managing dynamic arrays and linked lists
- Designing APIs that safely return or duplicate data
- Writing custom copy constructors for structs
Try It Yourself
- Create a struct with dynamically allocated fields (e.g.,
name,address) and write two copy functions:copy_shallow()andcopy_deep(). - Modify one copy and observe the difference.
- Call
free()in the wrong order and note what happens. - Use Valgrind to verify that deep copies are properly freed.
- Extend the concept to an array of structs, implement deep copy for each element.
When you understand deep vs shallow copies, you control how memory ownership moves in your program, a foundation for safe, modular, and leak-free C design.
