개발 공부/코딩 공부

복사와 함수 인자 전달 , 그리고 메모리

baby-t 2025. 9. 3. 15:37

프로그래머스 '마지막 두 원소' 문제

 

프로그래머스 '마지막 두 원소' 문제를 풀다가 간단한 코드 한 줄에서 생긴 의문

 

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)
복사 대상 메모리 주소 (참조) 실제 값 (데이터)
원본-사본 서로 영향을 줌 (데이터 공유) 서로 독립적 (완전한 분리)
안전성 메모리 문제 위험 안전함
Sheets로 내보내기

## 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