[C#] 값 형식과 참조 형식

Flaming Bun·2025년 4월 22일

C#

목록 보기
15/35

참고 영상: https://www.youtube.com/watch?v=LKyDIjYMGwc&list=PLVsNizTWUw7GN8wPRhclbKuQa9aI9Cj2V&index=4

🔥 값 형식과 참조 형식

값 형식(Value Type)

  • 값 형식은 변수에 값을 직접 저장한다.
  • 메모리의 스택에 할당된다.
  • 변수가 실제 데이터를 보유하고 있으며, 해당 변수를 다른 변수에 할당하거나 전달할 때는 값이 복사된다.
  • 값 형식 변수의 수정은 해당 변수의 값만 변경하므로 다른 변수에 영향을 주지 않습니다.
  • int, float, double, bool, 등 기본 데이터 형식과 구조체가 값 형식에 해당한다.

값형 예시

	// 값형
     int x = 10;
     int y = x;
     y = 20;
     Console.WriteLine("x: " + x); // 10
     Console.WriteLine("y: " + y); // 20

참조 형식(Reference Type)

  • 참조 형식은 변수가 데이터에 대한 참조(메모리 주소)를 저장합니다.
  • 메모리는 힙에 할당되어 가비지 콜렉터에 의해 제거된다.
  • 메모리를 많이 먹는 이미지나 클래스 같은 것들은 힙 영역에 저장하고, 주소를 가진 참조 변수를 스택에 쌓습니다.
    (이때 힙을 참조하고 있는 스택의 참조 변수가 사라지면 그때 가비지 콜랙터가 수거합니다.)
  • 변수가 실제 데이터를 가리키는 참조를 갖고 있으며, 해당 변수를 다른 변수에 할당하거나 전달할 때는 참조가 복사됩니다.
  • 참조 형식 변수의 수정은 동일한 데이터를 가리키고 있는 다른 변수에 영향을 줄 수 있습니다.
  • 배열, 인터페이스 등 복합 데이터 형식과 클래스 등이 참조 형식에 해당합니다.

⚔ 참조 형식 예시


// 참조형 1 - 배열
int[] arr1 = new int[] { 1, 2, 3 };
int[] arr2 = arr1;

arr2[0] = 4;

Console.WriteLine("arr1[0]: " + arr1[0]); // 4
Console.WriteLine("arr2[0]: " + arr2[0]); // 4

// 참조형 2 - 클래스
class MyClass
{
	public int Value;
}

MyClass obj1 = new MyClass();
obj1.Value = 10;
    
MyClass obj2 = obj1; // obj2는 obj1과 동일한 객체를 참조

obj2.Value = 20; // obj1과 동일한 객체를 참조하므로 값을 20으로 변경한다.

Console.WriteLine(obj1.Value); // 출력 결과: 20

기본 데이터 형식(Primitive Types)

  • C#이 제공하는 기본 데이터 형식
    • 수 형식
    • 논리 형식
    • 문자열 형식
    • object 형식
  • 복합 데이터 형식은 기본 데이터 형식을 바탕으로 만들어진다.
    • 복합 데이터 : 클래스, 구조체, 튜플 등


🔥 박싱(Boxing), 언박싱(Unboxing)

박싱과 언박싱은 값형과 참조형 사이의 변환을 의미한다.

1) 박싱

  • 박싱은 값형을 참조형으로 변환하는 과정을 말한다.
  • 값 형식을 object 형식에 담아 힙 영역에 올린다.
  • 박싱을 통해 값형이 참조형 특징을 갖게 되며, 참조형 변수로 다뤄질 수 있다.
  • 박싱된 값형은 참조로 전달되므로 메모리 오버헤드가 발생할 수 있다.

📌 메모리 오버헤드란 ?

컴퓨터 시스템에서 프로그램이 실제로 사용하는 메모리 외에 추가로 소비되는 메모리를 말한다.

박싱을 하면 힙에 메모리를 할당하고, 힙에 스택의 값 복사가 일어나며 힙의 주소를 스택 메모리에 저장하면서 메모리 오버헤드가 발생한다.

2) 언박싱

  • 언박싱은 박싱된 객체를 다시 값형으로 변환하는 과정을 말한다.
  • 힙에 올라가 있는 데이터를 object에서 꺼내 값 형식으로 옮긴다.
  • 언박싱은 명시적으로 타입 캐스팅을 해야 하며, 런타임에서 타입 검사가 이루어집니다.
  • 잘못된 형식으로 언박싱하면 런타임 에러가 발생할 수 있습니다.

3) 박싱과 언박싱의 주요 특징

  • 박싱과 언박싱은 값형과 참조형 사이의 변환 작업이므로 성능에 영향을 미칠 수 있습니다.
  • 박싱된 객체는 힙 영역에 할당된다.
  • 박싱된 객체와 원래의 값형은 서로 독립적으로 값을 수정하더라도 상호간에 영향을 주지 않는다.

4) 사용예제

object는 .NET Common Type System(CTS)의 일부이며, 모든 클래스의 직간접적인 최상위 클래스입니다.

모든 클래스는 object에서 상속되며, object는 모든 형식을 참조할 수 있는 포괄적인 타입입니다.

5) 박싱을 사용하는 이유

값을 스택에 저장하면 자동메모리이기 때문에 수명을 정할 수 없다.
박싱을 사용하면 값을 힙 영역에 저장해서 가비지 콜렉터가 수거하기까지로 수명을 정할 수 있다.
또한, 아래 예시처럼 object로 변환을 해서 다양한 타입의 변수들을 하나의 List에 담을 수 있는 범용성을 가진다.

⚔ 박싱 예시

 static void Main(string[] args)
 {

     // 박싱과 언박싱
     int num1 = 10;
     object obj = num1; // 박싱
     int num2 = (int)obj; // 언박싱
     Console.WriteLine("num1: " + num1);
     Console.WriteLine("num2: " + num2);

     // List 활용 예제 -- 타입이 다른 변수를 하나의 리스트로 관리할 수 있다.
     List<object> myList = new List<object>();

     int intValue = 10;
     myList.Add(intValue); // int를 object로 박싱하여 추가

     float floatValue = 3.14f;
     myList.Add(floatValue); // float을 object로 박싱하여 추가

     // 언박싱: 참조 형식을 값형식으로 변환하여 사용
     int value1 = (int)myList[0]; // object를 int로 언박싱
     float value2 = (float)myList[1]; // object를 float로 언박싱
     
	 Console.WriteLine("value1: " + value1); // 10
     Console.WriteLine("value2: " + value2); // 3.14f
 }

0개의 댓글