Let's Girin!

[C#] 값 형식과 참조 형식/ 얕은 복사와 깊은 복사 본문

C#

[C#] 값 형식과 참조 형식/ 얕은 복사와 깊은 복사

window= 2022. 5. 30. 22:27

1. 값 형식과 참조 형식 

값 형식(Value Types) : 변수가 을 담는 데이터 형식, 스택 메모리 영역 

참조 형식(Reference Types) :  변수가 값 대신 값이 있는 곳의 위치(참조)를 담는 데이터 형식, 힙 메모리 영역


2. 스택(Stack)과 값 형식 

{
   int a = 100;
   int b = 200;
   int c = 300;
}

이 코드에 선언된 세 변수 a, b, c는 차례대로 스택에 쌓였다가 코드 블록이 끝나면서 스택에서 걷혀 제거된다.

            c : 300
            b : 200
            a : 100

▷ 값 형식의 변수는 모두 이 스택에 저장된다. 코드 블록 안에서 생성된 모든 값 형식의 변수들은 프로그램 실행이 중괄호 "}"를 만나면 메모리에서 제거된다. 즉, 스택에 쌓인 데이터들은 코드 블록이 사라지는 시점에 함께 제거된다. 


3. 힙(Heap)과 참조 형식 

 힙은 저장된 데이터를 스스로 제거하지 못한다. 가비지 컬렉터(Garbage Collector)가 필요하다. 

 

* 가비지 컬렉터(GC)

☞ 프로그램 뒤에 숨어 동작하면서 힙에 더이상 사용하지 않는 객체가 있으면 그 객체를 쓰레기로 간주하고 수거하는 기능을 한다! 

 

 코드 블록이 끝나는 시점과 상관없이 데이터를 유지하고 싶을 때 힙 메모리 영역을 사용한다. 

 참조 형식의 변수는 힙과 스택을 함께 이용하는데 힙 영역에는 데이터를 저장하고,

    스택 영역에는 데이터가 저장된 힙 메모리의 주소를 저장한다. 

 

 데이터를 직접 저장하는 대신 실제 데이터가 저장된 메모리의 주소를 "참조"한다고 해서 "참조 형식"인 것이다! 

{
   object a = 10;
   object b = 20;
}

이 코드를 실행하면 실제 값 10, 20은 힙에 저장하고, a와 b는 값이 저장된 힙의 주소만 스택에 저장해둔다. 

b : &2000
a : &4000

                   [Stack]

20 (메모리 주소: 2000)
10 (메모리 주소: 4000)

                   [Heap]

 

코드 블록이 끝나는 지점에서 a와 b는 스택에서 사라지지만 힙에는 여전히 10과 20이 남게된다. 이 데이터들은 GC가 수거해 간다. 


4. 값 형식과 참조 형식 차이!

스택은 변수의 생명 주기가 다하면 자동으로 데이터를 제거하는 메모리 영역이다.

은 더이상 데이터를 참조하는 곳이 없을 때 가비지 컬렉터(GC)가 데이터를 치워주는 구조의 메모리 영역이다.


5. 얕은 복사

//클래스 선언 
class MyClass
{
   public int MyField1;
   public int MyField2;
}
{
   MyClass source = new MyClass();
   source.MyField1 = 10;
   source.MyField2 = 20;
   
   MyClass target = source;
   target.MyField2 = 30;
   
   Console.WriteLine("{0} {1}", source.MyField1, source.MyField2);
   Console.WriteLine("{1} {1}", target.MyField1, target.MyField2);
}

클래스참조 형식이다. 

 

① source 객체를 할당한다. 

source         
MyField1 = 10
MyField2 = 20

② source를 복사해서 받은 target은 힙에 있는 실제 객체가 아닌 스택에 있는 참조를 복사해서 받는다. 

    source              
target           
MyField1 = 10
MyField2 = 20

③ source와 target이 같은 메모리를 바라보게 된다. 

④ 따라서 위의 코드는 target의 MyField2를 30으로 바꿨는데 source의 MyField2도 30으로 바뀌게 된다. 

    결과값은 

    10     30

    10     30

    

⑤ 이렇게 객체를 복사할 때 참조만 살짝 복사하는 것얕은 복사(Shallow Copy)라고 한다. 


6. 깊은 복사 

class MyClass
{
   public int MyField1; 
   public int MyField2; 
   
   public MyClass DeepCopy()
   {
      MyClass newCopy = new MyClass();
      newCopy.MyField1 = this.MyField1; 
      newCopy.MyField2 = this.MyField2;
      return newCopy;
   }
}

① 객체를 힙에 새로 할당해서 그곳에 자신의 멤버를 일일이 복사해 넣는다. 

힙에 보관되어 있는 내용을 객체로부터 복사해서 받아 별도의 힙 공간에 객체를 보관하는 것이 깊은 복사(Deep Copy)이다. 

 

7. 얕은 복사 VS 깊은 복사 예제

using System;

namespace DeepCopy
{
   class MyClass
   {
      public int MyField1; 
      public int MyField2; 
      
      public MyClass DeepCopy()
      {
         MyClass newCopy = new MyClass();
         newCopy.MyField1 = this.MyField1; 
         newCopy.MyField2 = this.MyField2; 
         
         return newCopy;
      }
   }
   
   class MainApp
   {
      static void Main(string[] args)
      {
         Console.WriteLine("Shallow Copy");
         {
            MyClass source = new MyClass();
            source.MyField1 = 10;
            source.MyField2 = 20;
            
            MyClass target = source;
            target.MyField2 = 30;
            
            Console.WriteLine($"{source.MyField1} {source.MyField2}");
            Console.WriteLine($"{target.MyField1} {target.MyField2}");
         }
         
         Console.WriteLine("Deep Copy");
         {
            MyClass source = new MyClass();
            source.MyField1 = 10;
            source.MyField2 = 20;
            
            MyClass target = source.DeepCopy();
            target.MyField2 = 30;
            
            Console.WriteLine($"{source.MyField1} {source.MyField2}");
            Console.WriteLine($"{target.MyField1} {target.MyField2}");
         }
      }
   }
}

▷ 결과값

Shallow Copy
10 30
10 30
Deep Copy
10 20
10 30

 

반응형

'C#' 카테고리의 다른 글

[C#] is 키워드  (0) 2024.04.18
[C#] 클로저(Closure)  (0) 2023.09.21
[C#] 구조체와 클래스의 차이  (2) 2022.09.14