배열(Array) — 같은 타입의 값을 묶어 저장하는 참조 타입
배열(Array) — 같은 타입의 값을 묶어 저장하는 참조 타입
프로그램을 짜다 보면 같은 성격의 값을 여러 개 들고 있어야 할 때가 많아요. 과목별 점수, 일주일의 요일 이름, 어떤 목록의 항목들 같은 거죠. 그럴 때 값을 하나하나 변수로 만들기보다, 같은 타입의 여러 변수를 묶어서 하나의 자료구조로 관리하면 훨씬 편합니다. C#에서는 이걸 배열(Array)이라고 불러요. 배열은 데이터(값)를 저장하는 방식 중에서도 가장 기본적이면서 널리 쓰이는 형태입니다.
출처: Microsoft Learn
본문
배열은 같은 타입의 변수 여러 개를 하나의 데이터 구조로 묶어 저장할 수 있게 해 줍니다. 배열을 선언할 때는 요소(element)의 타입을 지정해 주면 돼요. 만약 어떤 타입이든 담고 싶다면, 타입을 object로 지정하면 됩니다. C#은 **통합 타입 시스템(unified type system)**을 쓰기 때문에, 미리 정의된 타입이든 사용자가 만든 타입이든, 참조 타입이든 값 타입이든 모든 타입이 직간접적으로 xref:System.Object를 상속받습니다.
C#에서 배열을 선언하는 가장 기본적인 형태는 이렇게 생겼습니다.
type[] arrayName;
배열은 참조 타입이라서 그 자체를 nullable 참조 타입으로 선언할 수 있어요. 요소의 타입 역시 참조 타입이 될 수 있으니, 배열을 nullable 참조 타입 요소들을 담는 형태로도 만들 수 있죠. 아래 선언들은 배열 자체와 요소의 널 허용 여부를 각각 어떻게 표기하는지 보여 줍니다.
type?[] arrayName; // non nullable array of nullable element types.
type[]? arrayName; // nullable array of non-nullable element types.
type?[]? arrayName; // nullable array of nullable element types.
배열을 만들었을 때 초기화하지 않은 요소들은 해당 타입의 **기본값(default value)**으로 설정됩니다.
int[] numbers = new int[10]; // All values are 0
string[] messages = new string[10]; // All values are null.
[!IMPORTANT] In the preceding example, even though the type is
string[], an array of non-nullable strings, the default value for each element is null. The best way to initialize an array to non-null values is to use a collection expressions.
배열이 가지는 성질을 정리해 보면 다음과 같습니다.
- 배열은 단일 차원, 다차원, 아니면 가변 배열(jagged) 중 하나가 될 수 있어요.
- 차원의 개수는 배열 변수를 선언할 때 정합니다. 그리고 각 차원의 길이(크기)는 배열 인스턴스를 만드는 시점에 정하죠. 이 값들은 인스턴스가 살아 있는 동안 바꿀 수 없습니다.
- 가변 배열은 '배열의 배열'입니다. 각 멤버 배열의 기본값은
null이에요. - 배열은 0부터 센다(zero-based). 요소가
n개라면 인덱스는0부터n-1까지입니다. - 배열의 요소는 어떤 타입이든 될 수 있어요. 배열 타입 자체도 요소가 될 수 있습니다.
- 배열 타입은 추상 기본 타입인 xref:System.Array에서 파생된 참조 타입입니다. 모든 배열은 xref:System.Collections.IList와 xref:System.Collections.IEnumerable을 구현합니다. 그래서 foreach 문으로 배열을 순회할 수 있어요. 단일 차원 배열은 여기에 더해 xref:System.Collections.Generic.IList`1와 xref:System.Collections.Generic.IEnumerable`1도 구현합니다.
배열을 만들 때 요소를 알려진 값들로 초기화할 수도 있습니다. C# 12부터는 컬렉션 식(Collection expression)으로 모든 컬렉션 타입을 초기화할 수 있어요. 초기화하지 않은 요소들은 기본값, 즉 0비트 패턴으로 설정됩니다. 모든 참조 타입(널 허용이 아닌 타입 포함)은 null을 값으로 갖고, 모든 값 타입은 0비트 패턴을 갖습니다. 이 말은 xref:System.Nullable`1.HasValue?displayProperty=nameWithType 속성이 false가 되고 xref:System.Nullable`1.Value?displayProperty=nameWithType 속성은 정의되지 않은 상태라는 뜻이에요. .NET 구현에서는 Value 속성에 접근하면 예외를 던집니다.
다음 예시는 단일 차원, 다차원, 가변 배열을 각각 만들어 봅니다.
:::code language="csharp" source="./snippets/shared/Arrays.cs" id="DeclareArrays":::
[!IMPORTANT] Many of the examples in this article use collection expressions (which use square brackets) to initialize the arrays. Collection expressions were first introduced in C# 12, which shipped with .NET 8. If you can't upgrade to C# 12 yet, use
{and}to initialize the arrays instead.// Collection expressions: int[] array = [1, 2, 3, 4, 5, 6]; // Alternative syntax: int[] array2 = {1, 2, 3, 4, 5, 6};
단일 차원 배열 (Single-dimensional arrays)
단일 차원 배열은 같은 종류의 요소들이 순서대로 나열된 모음입니다. 요소에 접근할 때는 **인덱스(index)**를 쓰는데, 이 인덱스는 그 요소가 배열 안에서 몇 번째 자리에 있는지를 뜻해요. 배열의 첫 번째 요소는 인덱스 0입니다. 단일 차원 배열은 new 연산자로 만들며, 요소 타입과 요소 개수를 지정해 주면 됩니다. 다음 예시는 단일 차원 배열을 선언하고 초기화하는 모습입니다.
:::code language="csharp" source="snippets/shared/Arrays.cs" id="SingleDimensionalArrayDeclaration":::
첫 번째 선언은 5개의 정수를 담는 배열을 초기화 없이 선언하는데, array[0]부터 array[4]까지 총 다섯 칸이 생깁니다. 이 배열의 요소들은 요소 타입의 기본값, 즉 정수라면 0으로 초기화돼 있어요. 두 번째 선언은 문자열 배열을 선언하면서 일곱 개 값을 모두 초기화합니다. 이어지는 Console.WriteLine 문들은 weekDay 배열의 모든 요소를 출력해요. 단일 차원 배열에서 foreach 문은 인덱스 0부터 시작해 Length - 1까지, 인덱스가 커지는 순서대로 요소를 처리합니다.
인덱스에 대해 더 자세히 알고 싶다면 Explore indexes and ranges 문서를 참고하세요.
단일 차원 배열을 인수로 전달하기 (Pass single-dimensional arrays as arguments)
초기화된 단일 차원 배열을 메서드에 전달할 수 있습니다. 다음 예시에서는 문자열 배열을 초기화해서 문자열용 DisplayArray 메서드에 인수로 넘깁니다. 이 메서드는 배열의 요소를 화면에 보여 줘요. 그다음 ChangeArray 메서드가 배열 요소의 순서를 뒤집고, ChangeArrayElements 메서드는 배열의 처음 세 요소를 수정합니다. 각 메서드가 끝난 뒤 다시 DisplayArray를 호출하면, 배열을 값으로 전달해도 요소까지 보호되지 않는다는 점이 드러납니다. 즉 배열은 참조 타입이기 때문에 메서드 안에서 요소를 바꾸면 원본 배열에도 그대로 반영되죠.
:::code language="csharp" source="./snippets/shared/ArrayExample.cs":::
다차원 배열 (Multidimensional arrays)
배열은 차원을 둘 이상 가질 수 있어요. 예를 들어 아래 선언들은 배열 네 개를 만드는데, 두 개는 2차원, 두 개는 3차원입니다. 처음 두 선언은 각 차원의 길이만 정하고 값을 초기화하지는 않아요. 뒤의 두 선언은 초기화 식을 사용해 다차원 배열의 각 요소 값을 직접 설정합니다.
:::code language="csharp" source="./snippets/shared/Arrays.cs" id="MultiDimensionalArrayDeclaration":::
다차원 배열을 순회할 때는 가장 오른쪽 차원의 인덱스부터 1씩 늘려가다가, 그다음 왼쪽 차원으로, 이런 식으로 맨 왼쪽 인덱스까지 진행합니다. 아래 예시는 2차원 배열과 3차원 배열을 각각 열거해 봅니다.
:::code language="csharp" source="./snippets/shared/Arrays.cs" id="ForeachMultiDimension":::
2차원 배열에서는 왼쪽 인덱스를 행(row), 오른쪽 인덱스를 **열(column)**로 생각하면 이해하기 쉬워요.
다만 다차원 배열을 처리할 때는 중첩된 for 루프를 쓰면 요소를 처리하는 순서를 더 자유롭게 제어할 수 있습니다.
:::code language="csharp" source="./snippets/shared/Arrays.cs" id="ForMultiDimension":::
다차원 배열을 인수로 전달하기
초기화된 다차원 배열을 메서드에 전달하는 방법은 1차원 배열과 똑같습니다. 아래 코드는 2차원 배열을 인수로 받는 Print 메서드의 선언 일부를 보여 줘요. 새 배열을 만들면서 바로 전달할 수도 있는데, 다음 예시가 그 모습입니다. 정수 2차원 배열을 초기화해 Print2DArray 메서드에 넘기면, 메서드가 배열의 요소를 화면에 표시해 줍니다.
:::code language="csharp" source="./snippets/shared/Arrays.cs" id="MultiDimensionParameter":::
가변 배열 (Jagged arrays)
**가변 배열(jagged array)**은 요소가 배열인 배열인데, 이 배열들은 크기가 서로 다를 수 있어요. 그래서 가끔 "배열의 배열(array of arrays)"이라고도 불립니다. 가변 배열의 요소는 참조 타입이라 기본적으로 null로 초기화돼요. 아래 예시들은 가변 배열을 선언하고, 초기화하고, 접근하는 각각의 방법을 보여 줍니다. 첫 번째 예시의 jaggedArray는 한 문장으로 선언되고, 각각의 내부 배열은 이어지는 문장에서 따로 만들어져요. 두 번째 예시의 jaggedArray2는 한 문장에서 선언과 초기화를 동시에 합니다. 가변 배열과 다차원 배열을 섞어 쓸 수도 있는데, 마지막 예시의 jaggedArray3가 바로 그 경우예요. 서로 크기가 다른 2차원 배열 세 개를 요소로 담는 단일 차원 가변 배열을 선언·초기화한 형태입니다.
:::code language="csharp" source="./snippets/shared/Arrays.cs" id="JaggedArrayDeclaration":::
가변 배열의 요소는 사용하기 전에 반드시 초기화해야 합니다. 각 요소가 그 자체로 배열이기 때문이죠. 초기화 식을 쓰면 배열 요소에 값을 채우는 것도 가능하고요. 이때는 배열 크기를 직접 알려 줄 필요가 없습니다.
아래 예시는 요소들이 각각 크기가 다른 배열로 이루어진 배열을 만듭니다.
:::code language="csharp" source="./snippets/shared/Arrays.cs" id="TrulyJagged":::
암시적 타입 배열 (Implicitly typed arrays)
배열 초기화 식에 적어 준 요소들로부터 배열 인스턴스의 타입을 추론하는 암시적 타입 배열을 만들 수도 있습니다. 암시적 타입 변수에 적용되는 규칙이 암시적 타입 배열에도 그대로 적용돼요. 자세한 내용은 Implicitly Typed Local Variables 문서를 참고하세요.
아래 예시들은 암시적 타입 배열을 만드는 방법을 보여 줍니다.
:::code language="csharp" source="./snippets/shared/Arrays.cs" id="LINQAndArrays":::
앞의 예시에서 눈여겨볼 점이 있어요. 암시적 타입 배열에서는 초기화 문장의 왼쪽에 대괄호가 없다는 겁니다. 또 가변 배열도 단일 차원 배열처럼 new []를 써서 초기화한다는 점도 기억해 두면 좋아요.
배열을 포함하는 익명 타입(anonymous type)을 만들 때는, 그 객체 초기화 식 안에서 배열이 반드시 암시적 타입이어야 합니다. 아래 예시에서 contacts는 익명 타입들로 이루어진 암시적 타입 배열입니다. 각 익명 타입은 PhoneNumbers라는 배열을 하나씩 담고 있어요. 객체 초기화 식 안에서는 var 키워드를 쓰지 않는다는 점도 확인하면 좋겠네요.
:::code language="csharp" source="./snippets/shared/Arrays.cs" id="LINQInit":::
더 알아보기
- Explore indexes and ranges — 인덱스와 범위를 더 자세히 살펴볼 수 있는 문서예요.
- Collection expressions — C# 12에서 배열·컬렉션을 초기화하는 새 문법입니다.
- Implicitly Typed Local Variables — 암시적 타입(
var) 규칙에 대한 자세한 설명입니다. - Default values — 타입별 기본값에 대해 다루는 문서입니다.