using System;
namespace CSharp
{
// 🔹 구조체 (값 타입 - 스택)
struct Mage
{
public int hp;
public int attack;
}
// 🔹 클래스 (참조 타입 - 힙)
class Knight
{
public int hp;
public int attack;
// 🔹 깊은 복사 (Deep Copy) 메서드
public Knight Clone()
{
Knight knight = new Knight(); // 새로운 Knight 객체를 생성 (힙 메모리에 할당)
knight.hp = this.hp; // 현재 Knight 객체의 hp 복사
knight.attack = this.attack; // 현재 Knight 객체의 attack 복사
return knight; // 복사된 객체 반환
}
public void Move() { Console.WriteLine("Knight Move"); }
public void Attack() { Console.WriteLine("Knight Attack"); }
}
class Program
{
// 🔹 값 타입 (스택) - 복사본을 전달
static void KillMage(Mage mage)
{
mage.hp = 0; // mage 구조체의 사본을 변경 → 원본에는 영향 없음
}
// 🔹 참조 타입 (힙) - 원본을 변경
static void KillKnight(Knight knight)
{
knight.hp = 0; // knight 객체의 원본을 변경 (같은 객체를 참조)
}
static void Main(string[] args)
{
// 🔹 값 타입 (Struct - Stack)
Mage mage;
mage.hp = 100;
mage.attack = 50;
Mage mage2 = mage; // mage의 값을 mage2에 복사 (깊은 복사)
mage2.hp = 0; // mage2의 hp만 변경, 원본 mage는 영향 없음
// 🔹 참조 타입 (Class - Heap)
Knight knight = new Knight(); // 새로운 Knight 객체 생성 (힙 메모리에 할당)
knight.hp = 100;
knight.attack = 10;
//KillKnight(knight); // knight.hp = 0; 호출하면 원본 knight도 변경됨
// 🔹 얕은 복사 vs 깊은 복사
// Knight knight2 = knight; // 얕은 복사 (같은 객체를 참조)
Knight knight2 = knight.Clone(); // 깊은 복사 (새로운 객체 생성)
knight2.hp = 0; // knight2의 hp만 변경됨, knight는 영향 없음
}
}
}

| 구분 | 스택(Stack) 메모리 | 힙(Heap) 메모리 |
|---|---|---|
| 메모리 위치 | 고정된 크기의 메모리 영역 | 동적 크기의 메모리 영역 |
| 저장 대상 | 지역 변수, 매개변수, 값 타입(Struct) | 객체, 배열, 클래스 인스턴스(Reference Type) |
| 할당 방식 | 함수 호출 시 자동 할당 | new 키워드로 동적 할당 |
| 해제 방식 | 함수가 종료되면 자동 해제 | C#의 가비지 컬렉터(GC)가 자동 해제 |
| 메모리 속도 | 빠름 (자동 해제) | 상대적으로 느림 (GC 작동 시기까지 유지) |
| 메모리 특징 | LIFO (Last In, First Out) 구조 | 동적 크기 조절 가능 |
✅ 스택 → 빠른 할당/해제, 지역 변수 저장, 함수 종료 시 자동 삭제
✅ 힙 → 동적 할당, 참조 타입 저장, 프로그램 실행 중에도 유지 가능
struct Mage
{
public int hp; // 체력
public int attack; // 공격력
}
✅ 구조체(Struct) → 값 타입이므로 복사 시 새로운 객체가 생성됨
✅ 스택(Stack) 메모리에 저장 → Mage mage2 = mage; 하면 새로운 메모리가 할당됨
✅ Mage 구조체를 복사하면 hp와 attack 값이 그대로 복사되지만, 두 객체는 서로 영향을 주지 않음
Mage mage;
mage.hp = 100;
mage.attack = 50;
Mage mage2 = mage; // 값 복사
mage2.hp = 0; // mage2의 hp만 변경됨, 원본 mage는 영향 없음
class Knight
{
public int hp;
public int attack;
}
✅ 클래스(Class) → 참조 타입이므로 복사 시 같은 객체를 가리킴
✅ 힙(Heap) 메모리에 저장 → Knight knight = new Knight(); 해야 객체가 생성됨
✅ Knight knight2 = knight; 하면 knight2와 knight는 같은 객체를 공유
Knight knight = new Knight();
knight.hp = 100;
knight.attack = 10;
Knight knight2 = knight; // 얕은 복사
knight2.hp = 0; // knight2의 hp를 변경하면 knight도 변경됨
➡ knight2는 knight와 동일한 객체를 참조하기 때문에 하나를 변경하면 나머지도 변경됨 (얕은 복사).
public Knight Clone()
{
Knight knight = new Knight();
knight.hp = this.hp;
knight.attack = this.attack;
return knight;
}
✅ Clone() 메서드를 사용하여 새로운 객체를 생성하고 모든 속성을 복사
✅ Knight knight2 = knight.Clone(); 하면 knight2는 knight와 독립적인 객체
Knight knight = new Knight();
knight.hp = 100;
knight.attack = 10;
Knight knight2 = knight.Clone(); // 깊은 복사
knight2.hp = 0; // knight2의 hp만 변경됨, knight는 영향 없음
static void KillMage(Mage mage)
{
mage.hp = 0; // mage 구조체를 복사하여 사용 (원본 영향 없음)
}
static void KillKnight(Knight knight)
{
knight.hp = 0; // knight 객체의 참조를 전달받아 원본이 변경됨
}
✅ 구조체(값 타입) → KillMage(mage);를 호출해도 원본 mage는 변경되지 않음
✅ 클래스(참조 타입) → KillKnight(knight);를 호출하면 원본 knight의 hp가 0으로 변경됨
Mage mage;
mage.hp = 100;
KillMage(mage); // 호출 후에도 mage.hp == 100 (영향 없음)
Knight knight = new Knight();
knight.hp = 100;
KillKnight(knight); // 호출 후 knight.hp == 0 (원본 변경됨)