값형식/ 참조형식 (스택/힙)

개발조하·2023년 10월 23일
3

C#

목록 보기
1/11
post-thumbnail

c#은 데이터의 메모리 저장 방식에 따라 두 개의 타입으로 나뉜다!
: 값형식(value types) vs. 참조형식(reference types)

두 타입의 대표적인 차이점은 아래와 같다.

값형식(value types)참조형식(reference types)
변수가 값을 담는 데이터 형식변수가 값 대신 값이 있는 곳의 위치(주소)를 담는 데이터 형식(참조)
데이터를 스택(stack) 메모리 할당데이터를 힙(heap) 메모리 할당 (힙 메모리의 주소를 스택에 저장)

직접 코드를 작성하며 이해해보자!

1. 값형식

namespace TypeTest
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int hp = 100;
            Console.WriteLine($"1) 현재 player1의 HP = {hp}");
            PlayerHP(hp);
            Console.WriteLine($"4) 최종 player1의 HP = {hp}");
        }

        static int PlayerHP(int hp)
        {
            Console.WriteLine($"2) 현재 player1의 HP = {hp}");
            hp--;
            Console.WriteLine($"3) 공격 당한 player1의 HP = {hp}");
            return hp;
        }
    }
}

위 코드를 실행하면 아래와 같은 결과가 나온다.
여기서 주목해야 하는 부분은 "4) 최종 player1의 HP = 100"이다.
분명 PlayerHP() 메소드에서 hp--;해서 HP값이 99로 줄었었는데,
왜 최종 HP가 100으로 다시 돌아왔을까?

이를 이해하려면 먼저 '스택(stack)'의 구조를 이해해야 한다.

1.1 스택(stack)의 구조

스택(stack)은 '쌓다, 쌓이다, 포개지다'라는 뜻으로,
데이터(data)를 순서대로 쌓는 자료구조이다.
호출(call)되면 PUSH()로 데이터가 스택에 쌓이고,
반환(return)될 때 POP()으로 데이터가 빠져나가 소멸된다.

스택(stack)

  • 함수의 호출과 관계되는 '지역 변수'와 '매개 변수'가 저장되는 영역
  • 함수의 호출과 함께 할당, 함수가 완료되면 소멸
  • PUSH 동작으로 데이터를 저장, POP 동작으로 데이터를 인출
  • 코드 블록 안에서 생성된 모든 값 형식의 변수들은 프로그램 실행이 코드 블록을 닫는 중괄호 }를 만나면 메모리에서 제거됨.
  • 값형식의 데이터가 저장되는 영역
    ㄴ int, double, char, bool, float 등, struct(구조체), enum

1.2 예시 해설

다시 예시로 돌아와서,
왜 최종 HP가 100으로 다시 돌아왔을까?를 살펴보자.
즉, PlayerHP() 메소드가 호출되면서 스택에 hp = 99;라는 데이터가 쌓였지만, 함수가 return될 때(끝날 때) 해당 데이터는 pop()되어 소멸되었다.
Main()함수로 돌아왔을 때 stack에 남아있는 데이터는 'hp = 100;' 뿐이기 때문에 '4)최종 player1의 HP = 100'으로 출력된 것이다.

2. 참조형식

namespace TypeTest
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Human hum = new Human();
            hum.Name = "Kim";
            Console.WriteLine(hum.Name);
            ChangeName(hum);
            Console.WriteLine(hum.Name);

        }

        static void ChangeName(Human hum)
        {
            hum.Name = "Park";
        }

    }
    /// <summary>
    /// 참조 형식으로 Human 클래스이다.
    /// </summary>
    class Human
    {
        public string Name { get; set;}
    }
}

위 코드를 실행하면 아래와 같은 결과가 나온다.

이 결과를 보면 위에 설명했던 '값형식'과 다른 논리임을 알 수 있다.
'값형식'의 논리로 따지면 ChangeName()메소드에서 Name="Park"로 바꿨다고 하더라도 Main()함수에는 영향을 주지 않고 "Kim"으로 출력되어야 한다.

하지만, 함수가 끝난 다음에 실행된 Console.WriteLine(hum.Name);는 바뀐 이름인 "Park"가 출력되었다. 즉, Main()에 영향을 주었다. 왜일까?를 살펴보자.

이를 이해하려면 먼저 '힙(heap)'의 구조를 이해해야 한다.

2.1 힙(heap)의 구조

참조형식은 '힙'영역에 데이터가 저장이 되고, '스택' 영역에서는 데이터가 저장되어 있는 힙 영역의 메모리의 주소를 저장한다.
즉, 스택 영역에 실제로 값을 가지고 있는 게 아니라 힙 영역의 데이터를 참조하고 있어 "참조 형식"이라고 한다.

힙(heap)

  • 동적으로 할당된 변수
  • 사용자가 직접 관리할 수 있고, 관리해야만 하는 메모리 영역
    ㄴ 사용자에 의해 메모리 공간이 동적으로 할당되고 해제된다.
  • 더 이상 데이터를 참조하는 곳이 없을 때 가비지 컬렉터가 데이터를 치워주는 구조
  • 참조형식의 데이터가 저장되는 영역
    ㄴ object, string, dynamic, class, interface, delegate, 배열
    ㄴ 멤버변수, class

2.2 예시 해설

다시 예시로 돌아와서,
Main()에 영향을 줘서 Name = "Park"가 출력되었다. 왜일까?를 살펴보자.
즉, Human클래스의 인스턴스가 생성될 때 heap에 Human클래스의 데이터가 생성되고, 해당 주소값이 stack에 변수명과 함께 저장된다. ChangeName()함수가 실행되어 hum.Name이 "Park"으로 변경되면, Heap에서 "Kim"값을 가르키던 Human클래스가 "Park"값을 가리키면서 Human클래스의 hum.Name의 값을 Park으로 완전히 바꿔버려서 main()에서도 바뀐 값이 출력되는 것이다.

3. 헷갈리는 포인트 잡기

그렇다면,
'값형식 -> 스택 / 참조형식 -> 힙'의 공식으로 외우면 될까??
절대 아니다.
위 코드를 살펴보자

  • class는 '참조형식'이다.
    ㄴ 그렇다면 Human hum = new Human();으로 인스턴스를 생성할 때,
    Human클래스는 Heap에 메모리 영역을 차지하고, stack에 hum변수명과 함께 주소를 남긴다.

  • Human클래스가 가진 변수 Age와 Name의 값은 각각 int(값형식), string(참조형식)타입이다.
    '값형식 -> 스택 / 참조형식 -> 힙' 이 공식대로라면 Age = 10;라는 데이터는 stack에만 저장되어야 한다는 것;;;
    -> 그러므로 '값형식 -> 스택 / 참조형식 -> 힙' 이렇게 공식처럼 외우는 것은 오히려 혼란만 가중시킨다!

    '값형식은 값을 바로 저장하고! 참조형식은 가리킬 주소를 저장한다!'라고 이해하면 쉽다.

위 코드로 이해하면, Human클래스의 인스턴스가 생성될 때 Age = 10이라는 데이터는 Human클래스가 차지한 영역에 바로 데이터를 입력했고, Name은 "홍길동"이라는 데이터가 있는 주소를 참조만 했다. 즉, "홍길동"은 heap의 다른 영역에 메모리를 차지하고 있는 것이다.

📄참고자료

profile
Unity 개발자 취준생의 개발로그, Slow and steady wins the race !

4개의 댓글

comment-user-thumbnail
2023년 10월 23일

멋져요~🙏🏻

1개의 답글
comment-user-thumbnail
2023년 10월 23일

정리를 일목요연하게 잘하셨네요 ~ 👍

1개의 답글