
프로그래머스 '마지막 두 원소' 문제를 풀다가 간단한 코드 한 줄에서 생긴 의문
vector<int> answer = num_list;
이 코드를 실행하면, answer는 num_list와 같은 메모리를 가르킬까?..
정답은 X.
std::vector는 대입 시 **깊은 복사(Deep Copy)**가 일어나기 때문이다. 이 간단한 의문은 C++의 핵심 개념인 객체 복사, 함수 인자 전달, 그리고 메모리 구조로 이어진다.
## 1. 모든 것의 기반: C++ 프로그램의 메모리 구조

깊은 복사와 얕은 복사를 이해하려면, 먼저 프로그램의 데이터가 어디에 저장되는지 알아야 합니다. 프로그램이 실행되면 운영체제는 메모리 공간을 할당하며, 이 공간은 주로 4개의 영역으로 나뉩니다.
- 코드(Code) 영역: 실행할 프로그램의 기계어 명령어가 저장되는 읽기 전용 공간입니다.
- 데이터(Data) 영역: 전역 변수와 정적(static) 변수가 저장되며, 프로그램 시작부터 끝까지 유지됩니다.
- 힙(Heap) 영역: 프로그래머가 **동적으로 할당(new)하고 해제(delete)**하는 메모리 공간입니다. 런타임에 크기가 결정되며, 유연하지만 관리가 필요합니다.
- 스택(Stack) 영역: 함수의 호출 정보, 매개변수, 지역 변수가 저장됩니다. 함수가 시작될 때 생성되고 끝나면 자동으로 소멸되며, 매우 빠르지만 크기에 제한이 있습니다.
## 2. 객체 복사 방식: 깊은 복사 vs. 얕은 복사
메모리 구조를 바탕으로, 객체를 복사하는 두 가지 방식을 알아보겠습니다.
### 얕은 복사 (Shallow Copy)
얕은 복사는 객체를 복사할 때, 데이터가 저장된 메모리의 주소 값만 복사합니다.
- 동작: 두 변수가 하나의 데이터를 공유하게 됩니다. 주로 포인터를 다룰 때 발생합니다.
- 결과: 한쪽에서 데이터를 변경하면 다른 쪽도 영향을 받습니다.
- 치명적 단점: 같은 메모리를 두 번 해제하는 **이중 해제(Double Free)**나, 이미 해제된 메모리를 가리키는 **댕글링 포인터(Dangling Pointer)**와 같은 심각한 메모리 문제를 일으킬 수 있습니다. 💥
### 깊은 복사 (Deep Copy)
깊은 복사는 데이터 자체를 통째로 복사하여 완전히 독립적인 객체를 만듭니다.
- 동작: 새로운 메모리 공간을 힙에 할당하고 원본의 내용을 그대로 복사합니다.
- 결과: 원본과 사본은 완전히 분리되어, 서로에게 전혀 영향을 주지 않습니다. std::vector를 포함한 대부분의 C++ 표준 라이브러리 컨테이너는 안전을 위해 깊은 복사를 기본으로 합니다.
| 구분 | 얕은 복사 (Shallow Copy) | 깊은 복사 (Deep Copy) |
| 복사 대상 | 메모리 주소 (참조) | 실제 값 (데이터) |
| 원본-사본 | 서로 영향을 줌 (데이터 공유) | 서로 독립적 (완전한 분리) |
| 안전성 | 메모리 문제 위험 | 안전함 |
## 3. 함수 인자 전달 방식: Call by Value vs. Call by Reference
이제 함수에 인자를 전달할 때 메모리가 어떻게 사용되는지 알아보겠습니다.
### Call by Value (값에 의한 호출)
함수가 호출될 때, 매개변수는 호출된 함수의 스택(Stack)에 새롭게 생성됩니다.
- void function(vector<int> num_list)
- function의 스택에 num_list라는 새로운 vector 객체가 생성됩니다.
- 이 과정에서 외부 벡터의 내용이 깊은 복사되어, 힙(Heap)에도 새로운 데이터 공간이 할당됩니다.
- 장단점: 원본 데이터의 안전성이 보장되지만, 큰 객체를 전달할 경우 복사 비용이 많이 듭니다.
### Call by Reference (참조에 의한 호출)
함수의 스택에 원본 데이터를 가리키는 참조자(주소)만 생성됩니다.
- void function(vector<int>& num_list)
- 새로운 객체나 데이터가 전혀 생성되지 않습니다. 함수 내의 num_list는 원본 객체의 별칭(alias)일 뿐입니다.
- 장단점: 복사 비용이 없어 매우 효율적이지만, 함수 내에서 원본 데이터를 수정할 수 있는 부수 효과(Side Effect)가 발생할 수 있습니다. (읽기 전용으로 사용 시 const 키워드를 붙이는 것이 좋습니다.)
## 4. 결론: 그래서 answer = num_list는?
처음의 의문으로 돌아가 봅시다.
간단한 문제지만, 의문점이 드는 이유는 객체 복사와 함수 인자 전달을 혼용하여 생각했기 때문입니다.
answer = num_list는 객체 복사에 해당하며, std::vector의 대입 연산자는 깊은 복사로 구현되어 있습니다. 따라서 answer는 num_list와 완전히 독립적인 새로운 객체로 생성됩니다.
void function(vector<int> num_list)와 같이 Call by Value로 함수를 호출하는 것은, 함수가 시작될 때 내부적으로 매개변수 num_list를 생성하면서 num_list = original_list와 같은 깊은 복사가 일어나는 과정이라고 이해할 수 있습니다.
이처럼 메모리 구조를 기반으로 객체 복사와 함수 호출 방식을 연결하여 이해하면, 코드의 동작을 더 명확하게 예측할 수 있습니다.
'개발 공부 > 코딩 공부' 카테고리의 다른 글
| C++ 람다식 (0) | 2025.09.16 |
|---|---|
| C++ 입출력 (0) | 2025.09.15 |
| 프로그래머스 리스트 자르기 (0) | 2025.09.13 |
| c++ <limits>, const &auto (0) | 2025.09.03 |
| 간단한 수학 공식들 (1) | 2025.09.01 |