본문 바로가기
C#

구조체 배열 선언 시, 메모리에서 일어나는 과정

by Oz Driver 2025. 7. 2.

 

구조체 배열을 선언하면 메모리 관점에서 어떤 일이 발생하는지 그 과정에 대해 한번 알아보겠습니다.

 

배열 선언과 초기화 그리고 내부 동작 

먼저, 다음과 같은 예제 코드가 있다고 해보겠습니다.

public struct StageData
{
    public int ballSpeed;
    public int blockColumn;
    public int blockRow;
}

StageData[] arr = new StageData[5];

 

•   new StateData 구조체를 크기 5 인 배열로 선언합니다. 

•   StageData[]는 참조 타입 (배열은 참조 타입입니다.) 이므로 힙에 5개짜리 배열 공간이 생성됩니다.

•   그리고 StageData는 구조체(값 타입) 이므로, 배열 안에는 구조체가 들어갑니다. 

•   즉, 힙에 구조체 5개 짜리 데이타 공간이 생성되는 것입니다. 

 

각 요소는 자동 초기화

•  new StageData[5]를 하면, C#은 각 구조체를 default 값으로 자동 초기화합니다.

•  따라서, arr[0]부터 arr[4]까지는 모두 다음 값으로 초기화됩니다. 

•  만약 구조체 안에 참조 타입 필드가 있으면, 그 값은 null로 초기화됩니다.

ballSpeed = 0
blockColumn = 0
blockRow = 0

 

new 에 공간을 생성하거나, 스택이라면 해당 크기 만큼 공간을 사용할 수 있도록 하는 역할만을 합니다. 

그리고 new 뒤에 따라오는 [ ] 나 ( ) 또는 { } 로 default 생성자 등을 호출하여 값을 자동 초기화합니다. 

// 컴파일 오류 (X)
StageData stageData = new StageData;

 

만약, new 가 값을 초기화하는 기능까지 있다면, 위와 같은 코드가 성립되어야 하는데 이런 코드는 없습니다.

즉, new 단독으로는 쓰일 수가 없고 뒤에 초기화하는 무언가가 반드시 뒤따라 오게 되어 있습니다. 그렇게 때문에 흔히 new 로 초기화 라는 말을 하는데, 엄밀히 말하면 new 로 먼저 공간 (참조 형식이면 힙에 동적으로 생성, 값 형식이라면 스택에 공간을 점유) 을 마련한 후에, 초기화 라는 말이 더 정확합니다만, 대부분은 (거의 모두에 가까움) 이렇게 말하지 않기 때문에 우리는 이것을 분리해서 잘 생각하지 않게 되는 것 같습니다. 

 

구조체 필드에 값 대입

어쨌거나, new StageData[5] 로 구조체의 각 필드는 0 으로 자동 초기화된 상태입니다.

이제, 원하는 값으로 초기화시켜야 합니다. 코드는 다음과 같습니다.

new StageData { ballSpeed = 5, blockColumn = 5, blockRow = 3 };

 

여기서 new 는 구조체 StageData 를 스택 또는 레지스터에 임시 저장 보관소를 만드는 역할을 합니다. 

공간만 마련된 상태이기 때문에, 이제 값을 채워넣어야 합니다.

다음 코드가 그 역할을 합니다.

{ ballSpeed = 5, blockColumn = 5, blockRow = 3 };

 

대입 연산자로 값 복사

이제 힙에 할당된 각 구조체에 값을 채워넣어야 합니다. 그리고 그 코드는 다음과 같습니다.

arr[0] = new StageData { ballSpeed = 5, blockColumn = 5, blockRow = 3 };

 

위 코드를 쪼개서 살펴보면, 

new StageData { ballSpeed = 5, blockColumn = 5, blockRow = 3 };

 

임시로 생성한 메모리 공간에 값을 채워넣은 후에 (이미 위에서 한번 설명했음)

arr[0] =

 

arr[0] 에 값을 대입합니다. 이 때 arr[0] 는 구조체이므로 값 복사가 일어납니다. 

결과적으로 힙 메모리에 생성된 구조체 arr[0] 에 값이 채워집니다.  

 

최종 코드

지금까지 설명한 내용을 코드로 작성하면 다음과 같습니다.

StageData[] stageDate = new StageData[5];
stageDate[0] = new StageData { ballSpeed = 5, blockColumn = 5, blockRow = 3 };
stageDate[1] = new StageData { ballSpeed = 6, blockColumn = 6, blockRow = 3 };
stageDate[2] = new StageData { ballSpeed = 7, blockColumn = 7, blockRow = 4 };
stageDate[3] = new StageData { ballSpeed = 8, blockColumn = 8, blockRow = 4 };
stageDate[4] = new StageData { ballSpeed = 9, blockColumn = 9, blockRow = 5 };

 

만약, 선언과 동시에 값을 초기화할 경우라면 다음과 같습니다. 

StageData[] stageDate = new StageData[5]
{
    new StageData { ballSpeed = 5, blockColumn = 5, blockRow = 3 },
    new StageData { ballSpeed = 6, blockColumn = 6, blockRow = 3 },
    new StageData { ballSpeed = 7, blockColumn = 7, blockRow = 4 },
    new StageData { ballSpeed = 8, blockColumn = 8, blockRow = 4 },
    new StageData { ballSpeed = 9, blockColumn = 9, blockRow = 5 },
};

 

그리고 구조체 크기만큼 이미 힙에 공간이 할당된 상태이기 때문에, 다음과 같이 직접 각 요소에 값을 넣어도 됩니다.

StageData[] stageDate = new StageData[5];     
          
stageDate[0].ballSpeed = 5;
stageDate[0].blockColumn = 5;
stageDate[0].blockRow = 3;

stageDate[1].ballSpeed = 6;
stageDate[1].blockColumn = 6;
stageDate[1].blockRow = 3;

stageDate[2].ballSpeed = 7;
stageDate[2].blockColumn = 7;
stageDate[2].blockRow = 4;

stageDate[3].ballSpeed = 8;                
stageDate[3].blockColumn = 8;
stageDate[3].blockRow = 4;

stageDate[4].ballSpeed = 9;
stageDate[4].blockColumn = 9;
stageDate[4].blockRow = 5;

 

정리

지금까지 구조체 배열 선언과 초기화에 대해 살펴보았습니다. 

처음에는 코드가 눈에 잘 들어오지 않지만 지금까지 설명한 내용을 이해하신다면, 전체 코드가 눈에 익숙해지실 거라 생각됩니다.

 

 

 

추가적으로...

C# 에서 구조체 배열 초기화는 방법이 다양해서 처음 보면 굉장히 헷갈릴 수 있습니다. 특히 C# 문법이 발전하면서 새로운 축약형 문법이 추가되었고, 그 결과 같은 의미를 갖는 코드라도 여러 방식으로 표현할 수 있게 되었습니다.

그래서 배열과 List<T> 에서의 초기화에 대해 정리해보았습니다.

 

배열과 List<T> 에서 중괄호 { } 의 역할

•   배열에서 중괄호 { }는 각 요소를 인덱스 순서대로 배열에 채워 넣는 역할을 합니다.
•    List<T>에서 중괄호 { }는 Add() 함수를 자동으로 호출해 요소를 추가하는 역할을 합니다.

 

1. 가장 기본적인 형태

Item[] items = new Item[]
{
    new Item { name = "Apple", price = 1000 },
    new Item { name = "Banana", price = 2000 }
};

 

2. 배열 생성자 생략

Item[] items =
{
    new Item { name = "Apple", price = 1000 },
    new Item { name = "Banana", price = 2000 }
};

 

3. 생성자 타입 생략 (C# 9.0 이상)

Item[] items =
{
    new() { name = "Apple", price = 1000 },
    new() { name = "Banana", price = 2000 }
};

 

4. List 형태로 초기화

List<Item> items = new List<Item>
{
    new Item { name = "Apple", price = 1000 },
    new Item { name = "Banana", price = 2000 }
};

또는 

List<Item> items = new()
{
    new() { name = "Apple", price = 1000 },
    new() { name = "Banana", price = 2000 }
};

 

자신이 선호하는 어떤 걸 써도 무방하나, 다양한 형태에 대해 눈으로 익혀둘 필요는 있겠습니다.