이전까지의 글에서 클래스(Class)와 구조체(Struct)에 대해서 다뤘었다.
해당 글에서도 둘의 차이에 대해 비교해 보았지만 이번 글에서는 이 차이점만을 다뤄보려 한다.
값 타입 (Value type) vs 참조 타입 (Reference Type)
값 타입 (Value Type)
- 스택(Stack)에 저장되는 데이터
- 데이터를 복사해서 전달
- 독립된 값을 가지므로, 다른 변수에 복사해도 원본과 무관
int a = 10;
int b = a; // 값 복사
b = 20;
Console.WriteLine(a); // 10
Console.WriteLine(b); // 20
참조 타입 (Reference Type)
- 힙(Heap)에 객체가 저장되고, 스택에는 그 주소(참조)만 저장
- 변수를 복사하면, 참조가 복사되므로 같은 객체를 가리킴
class Person
{
public string Name;
}
Person p1 = new Person();
p1.Name = "Alice";
Person p2 = p1; // 참조 복사
p2.Name = "Bob";
Console.WriteLine(p1.Name); // "Bob"
클래스(Class)와 구조체(Struct)의 차이
C#에서 클래스는 참조 타입, 구조체는 값 타입으로 정의된다.
class Car
{
public int Speed;
}
struct Point
{
public int X;
public int Y;
}
메모리 배치
- 클래스 (참조 타입)
- 힙에 실제 데이터 저장
- 스택에는 힙 주소만 저장
- GC가 관리
- 구조체 (값 타입)
- 스택에 직접 데이터 저장 (단, 배열·리스트에 들어가면 힙에 올라가기도 함)
- 값 자체를 복사해서 전달
생성자와 초기화
- 클래스: new 필요 (참조 생성)
- 구조체: new 없이도 선언 가능, 단 필드 초기화 필수
Car car = new Car(); // new 필수
Point p; // new 없어도 선언 가능
p.X = 10;
p.Y = 20;
상속 여부
- 클래스: 상속 가능 (class B : A)
- 구조체: 상속 불가, 다만 인터페이스 구현은 가능
class Animal { }
class Dog : Animal { } // 가능
struct MyStruct { }
// struct Child : MyStruct { } // 불가능
기본 생성자
- 클래스: 생성자 정의 가능, 기본 생성자 제공
- 구조체: 매개변수 없는 생성자 직접 정의는 C# 10 전까지 금지, 이후 허용
- 구조체는 항상 default(T)로 0 초기화 가능
Null 가능 여부
- 클래스: 참조 타입이므로 기본적으로 null 가능
- 구조체: 값 타입이므로 null 불가 (단, Nullable<T> 또는 T?로 가능)
Car car = null; // 가능
Point p = null; // 불가능
Point? p2 = null; // Nullable
박싱(Boxing)과 언박싱(Unboxing)
구조체(값 타입)를 object나 interface에 담을 때는 힙에 새로 할당 → 성능 저하 가능
int x = 10;
object o = x; // boxing
int y = (int)o; // unboxing
성능 차이
- 클래스(참조 타입)
- 장점: 상속, 다형성, 대형 객체 관리에 유리
- 단점: 힙 할당과 GC 비용, 참조 추적 필요
- 상속/다형성이 필요한 경우 추천
- 구조체(값 타입)
- 장점: 스택에 할당되어 빠르고, 작은 데이터 묶음 표현에 적합
- 단점: 값 복사 비용, 상속 불가, 박싱/언박싱 성능 저하 가능
언제 클래스, 언제 구조체?
- 구조체 추천
- 데이터 크기가 작고, 수명이 짧은 경우
- 좌표(Point), 색상(Color), 벡터(Vector3) 등 “단순 데이터 묶음”
- 클래스 추천
- 상속/다형성이 필요한 경우
- 데이터 크기가 크거나, 공유해야 하는 경우
- 복사보다 참조가 더 효율적인 경우
핵심 포인트 정리
- 클래스와 구조체 차이점?
→ 클래스는 참조 타입(힙), 구조체는 값 타입(스택), 상속 가능 여부 차이. - 값 타입과 참조 타입의 메모리 저장 위치?
→ 값 타입은 스택, 참조 타입은 힙 (단, 값 타입도 컬렉션 안에 들어가면 힙에 저장 가능). - 구조체를 언제 쓰는 게 좋은가?
→ 작은 데이터 묶음, 불변(immutable) 모델, 빈번히 생성/삭제되는 단순 객체. - 박싱/언박싱이 뭔가?
→ 값 타입을 참조 타입으로 변환할 때 힙 할당이 발생하는 것. 성능에 주의해야 함.
| 클래스(Class) | 구조체(Struct) | |
| 타입 | 참조 타입 (Reference Type) | 값 타입 (Value Type) |
| 메모리 저장 위치 | 힙(Heap)에 저장, 스택에는 참조(주소)만 저장 | 스택(Stack)에 직접 값 저장 (단, 배열·컬렉션 안에 있으면 힙에 저장되기도 함) |
| 생성자 | 명시적 기본 생성자 정의 가능, new 필수 | C# 10 이전까지 기본 생성자 정의 불가 (C# 10부터 허용), new 없이 선언 가능 |
| 상속 | 다른 클래스 상속 가능, 다형성 지원 | 상속 불가, 다만 인터페이스 구현은 가능 |
| null 할당 | 가능 (Car car = null;) | 불가능 (단, Nullable<T> 또는 T? 형태로 가능) |
| 초기화 | 객체 생성 시 new 필요 | 필드 직접 초기화 가능, default(StructName) 사용 가능 |
| 복사 방식 | 참조 복사 (같은 객체 공유) | 값 복사 (별도의 복사본 생성) |
| GC 영향 | 힙에 올라가므로 가비지 컬렉터가 관리 | 보통 스택에 할당되어 GC 관리 부담 적음 |
| 성능 특성 | 대형 객체 관리, 상속/다형성 활용에 유리 | 경량 데이터 묶음에 적합, 빠른 할당/해제 |
| 박싱/언박싱 | 없음 | 발생 가능 (object나 인터페이스에 담을 때 힙 할당 발생) |
'C#' 카테고리의 다른 글
| C# - 클래스 상속 (Inheritance) (0) | 2025.09.19 |
|---|---|
| C# - 클래스와 Static (0) | 2025.09.18 |
| C# - 메모리와 가비지 컬렉터 (GC) (0) | 2025.09.17 |
| C# - 클래스 실습 문제 풀이 (생성자) (0) | 2025.09.17 |
| C# - 생성자, 소멸자, 부분 클래스, 네임스페이스, 깊은 복사, 얕은 복사, 매개변수 한정자 (0) | 2025.09.16 |