기본 개발 지식

[C#] 메모리 영역, struct, class, transform, vector이해

한니닝 2024. 8. 25. 17:48

c#에서의 struct와 class차이에 대해 알아보자

*잠깐*

c++ 에서 struct(구조체)와 class(클래스)의 차이는 접근제어에 있다.

struct는 기본 public 이며 class는 private이다.

 


 

둘의 차이점을 알면 메모리를 절약할 수 있어 성능개선에 도움이 된다.

그럼 일단 그 메모리가 어떤건지에 대해 알아보자

메모리

프로그램을 실행 시키면 그 프로그램을 실행시키기 위해 메모리 공간을 할당해준다.

메모리 공간은 크게 스택(Stack), 힙(Heap)영역으로 나뉜다.

 

"c#에서는 value type(값)은 stack에 저장되고, reference type(참조)은 heap에 저장된다."


 

스택(Stack)

  • 함수 호출과 관계되는 지역 변수 매개변수가 저장되는 영역
  • 따라서 함수 호출과 함께 할당되며, 함수가 끝나면 소멸
  • 후입선출(LIFO, Last-In First-Out)로 가장 마지막에 저장된 데이터가 제일 먼저 나옴
  • 컴파일 타임에 크기가 결정된다.
  • Stack overflow : stack이 커지다가 heap 영역을 침범하는 것

스택 영역에 메모리가 할당되면 포인터가 적절한 양만큼 이동한다. 메로리가 해제되면 다시 포인터가 이동한다.

 

실제 스택 영역에서 메모리가 해제 될 때 저장된 값을 지우는 것이 아니라 포인터만 이동하고 해당 메모리가 다시 필요해질 때 엎어 쓰여지게 된다.

 

스택 영역에서의 메모리 할당과 해제는 힙 영역의 할당과 해제와 비교하여 매우 빠르다.

 

class Program{
    public int Plus(int a, int b){
        int result = a + b;
        return result;
    }
    public int Mul(int a, int b){
        int result2 = a * b;
        return result2;
    }
    static void Main(strinng[] args){
        int a = 1;
        Plus(a, 2);
        Mul(a, 3);
    }
}

대충 이런 코드가 있다고 해보자. 함수의 순서가 main -> plus -> mul 인 것을 볼 수 있다.

 

1. main함수가 호출되면 main 함수에 대한 지역 변수들이 스택에 할당된다. (변수a)

2. 다음 plus 함수가 호출되면 plus에 대한 지역변수들이 스택의 맨 위에 할당된다. (변수 result)

3. 그리고 plus 함수가 끝나고 다시 main함수로 돌아갈 때 함수 plus의 스택에 할당된 지역 변수(result)들은 버려지고, main의 지역변수들이 스택의 맨위에 다시 위치하게 된다 (후입선출)

 

따라서 함수 안 지역 변수들은 함수 내에서만 쓰이고, 함수 밖에서는 사용할 수 없는 것이다.

 

스택 프레임(Stack Frame)

함수를 호출하기 전에 함수에서 사용될 정보들이 스택에 저장되는데 이것들을 하나의 묶음으로 표현한게 스택 프레임이다. 하나의 함수에는 하나의 스택 프레임이 존재한다.

위의 코드처럼 Main()->Plus()->Mul() 순으로 시작하고 함수가 종료되는 순서는

Plus()->Mul()->Main()이다. 함수가 끝나는 순간 메모리에서 삭제 되기 때문에 메모리 공간을 효율적으로 사용할 수 있게 된다.


 

힙(Heap)

    • 필요에 의해 동적으로 메모리를 할당 할 때 사용
    • 사용자가 직접 관리. 사용자에 의해 메모리 공간이 동적으로 할당되고 해제됨
    • 메모리의 낮은 주소에서 높은 주소의 방향으로 할당된다.
    • 런 타임에 크기가 결정된다.
    • heap overflow: heap이 커지다가 stack 영역을 침범

힙 영역은 동적 할당을 위한 메모리 공간으로 단일 함수 수명을 넘어 존재해야 하는 객체를 할당하기 위해 필요하다.

힙 메모리는 필요 공간을 먼저 확보하고 임의의 위치에 할당한다.

스택은 함수가 끝나면 할당 되어 있던 메모리들이 소멸되지만, 힙에 할당 된 객체는 함수와 관계가 없어서 필요하지 않을 때 우리가 해제를 해주어야한다.

 


 

Value Type(값 유형)과 Reference Type(참조 유형)

아까 위에서 스택 영역에는 value type이 그리고 힙 영역에는 reference type이 저장된다고 했다.

여기서 reference type, 참조 타입은 해당 유형의 변수가 힙에 할당 되어 있는 객체를 가리키고 있는 포인터임을 의미한다.

c#에서 class는 참조 타입으로 생성되며 모두 힙 영역 메모리에 할당된다.

class유형인 모든 변수는 실제로 '포인터'라는 것

반면 c#에서 struct는 값 타입으로 객체에 대한 포인터가 아닌 객체 자체다.

struct를 함수의 지역 변수로 생성하면 해당 '객체'는 스택 메모리 영역에 할당된다.

struct가 class의 멤버 변수로 생성된다면 class와 같이 힙 메모리에 객체의 일부로 할당된다.

*헷갈리는 점*

c++에서 new 키워드는 힙 영역에 객체를 할당하겠다는 의미이지만

c#에서는 struct와 class모두 new 키워드를 이용해 생성된다.

 

유니티2D 게임을 만들면서 들었던 의문은

transform과 Vector3의 차이점이었다.

알고보니 c#에서 Vector는 구조체 타입이다.

위 설명에 따라 구조체 타입은 value type이므로 함수의 지역 변수로써 계속 new를 해도 스택에 저장되며 힙에 메모리 할당이 되지 않는다.

스택에 저장된 경우 함수가 종료되면 알아서 소멸된다.

즉, Update안에 new를 남발해도 현재 Update가 끝나면 그 new값은 해제가 되고 다시 생성되고를 반복하기 때문에 문제가 발생하지 않는다.

transform.position으로 가져오는 Vector3는 Vector3가 레퍼런스가 아니라서 복사된 값이 저장된 Vector3이다.

그래서 transform.position.Set()을 사용해도 복사된 값만 변경시키는 거라 적용이 안될 것이다. 그래서 이동을 해줄땐 transform.Translate()를 이용해 해준다.