배열은 한 타입의 요소를 정해진 개수만큼, 메모리에 나란히 저장합니다. 만들 때 길이를 정하고, 0부터 시작하는 인덱스로 요소를 읽고 쓰며, 그 뒤로 길이는 절대 바뀌지 않습니다.
배열 선언과 초기화
배열의 타입은 요소 타입 뒤에 []를 붙인 것입니다. 배열을 만드는 방법은 여러 가지입니다:
출력:
90, 0, 0, 72
Ben
2 prices, 3 days
new int[4]는 모든 요소를 타입의 기본값으로 채웁니다. 숫자는 0, bool은 false, 문자열과 그 밖의 참조 형식은 null입니다. 짧은 형태 { "Ana", "Ben" }은 선언에서만 쓸 수 있으며, 기존 변수에 대입할 때는 names = new[] { "Dev", "Eli" };가 필요합니다.
길이, 인덱싱, 마지막 요소
인덱스는 0부터 Length - 1까지입니다. Length는 속성이므로 (LINQ의 Count() 메서드와 달리) 괄호가 없습니다.
출력:
Count: 5
First: 18
Last: 16
Caught IndexOutOfRangeException
범위 밖을 읽거나 쓰면 실행 시점에 IndexOutOfRangeException이 발생하며, 컴파일러는 이를 잡지 못합니다. 흔한 원인은 루프 조건을 i < arr.Length 대신 i <= arr.Length로 쓴 것입니다.
C# 8 이상은 끝에서부터의 인덱스와 범위 문법을 추가했으며, 마지막 요소와 부분 배열을 더 짧게 쓸 수 있습니다:
int last = temps[^1]; // 16
int[] middle = temps[1..4]; // { 21, 25, 19 }, a new array
배열 순회하기
for 루프는 인덱스를 주고, foreach는 각 요소를 주며 읽기만 할 때 더 명확한 선택입니다.
출력:
4.95 | 13.20 | 3.58
Total: 21.73
foreach 변수는 읽기 전용입니다. 루프 안의 p = 0;은 컴파일 오류입니다. 요소를 바꾸려면 인덱스로 순회하세요.
정렬, 뒤집기, 검색
도우미 메서드는 정적 Array 클래스에 있습니다. Array.Sort는 제자리에서 정렬하고 아무것도 반환하지 않으므로 원래 순서는 사라집니다.
출력:
Ascending: 60, 72, 79, 88, 95
Descending: 95, 88, 79, 72, 60
Index of 60: 4
Index of 99: -1
Any above 90: True
First below 75: 72
Ana, ben, chloe
fig, kiwi, banana
Array.IndexOf는 값이 없으면 -1을 반환합니다. Array.Find는 일치하는 것이 없으면 타입의 기본값(여기서는 0)을 반환하는데, 숫자에서는 모호하므로 중요할 때는 Array.FindIndex를 쓰세요. 한 번의 호출로 내림차순 정렬하려면 Array.Sort(scores, (a, b) => b.CompareTo(a))입니다.
원본을 건드리지 않고 정렬된 결과가 필요하면 LINQ가 새 배열을 반환합니다: var sorted = scores.OrderBy(s => s).ToArray();. LINQ를 참고하세요.
크기 변경: 배열은 늘어날 수 없습니다
길이는 고정입니다. Array.Resize는 요청한 크기의 새 배열을 만들고, 요소를 복사하고, ref로 넘긴 변수를 갱신합니다.
출력:
4
2
True
False
크기를 바꿀 때마다 모든 요소가 복사됩니다. 루프에서 배열을 한 항목씩 늘리는 것은 느리며, 바로 이 일을 List<T>가 내부 배열을 두 배씩 늘려서 더 효율적으로 해 줍니다.
배열은 참조 형식입니다: 복사하기
배열을 다른 변수에 대입하면 요소가 아니라 참조가 복사됩니다. 그러면 두 이름이 같은 배열을 가리킵니다.
출력:
100
2
0, 100, 2, 3, 0
배열을 메서드에 넘길 때도 마찬가지로, 메서드가 호출하는 쪽의 요소를 바꿀 수 있습니다. Clone, Array.Copy, CopyTo, LINQ의 ToArray()는 모두 얕은 복사를 합니다. 객체 배열이라면 새 배열이 같은 객체에 대한 참조를 담으므로, copy[0].Name을 바꾸면 original[0]이 보는 것도 바뀝니다.
==로 배열을 비교하면 참조를 비교합니다. 내용을 비교하려면 System.Linq의 a.SequenceEqual(b)를 쓰세요.
2차원 배열(다차원 배열)
직사각형 격자는 대괄호 안에 쉼표를 넣어 선언합니다. [row, col]로 인덱싱합니다.
출력:
Rows: 2
Cols: 3
Length: 6, Rank: 2
. X .
. . X
2차원 배열의 Length는 전체 요소 수(2행 3열 격자라면 6)이므로, 중첩 루프는 GetLength(0)과 GetLength(1)을 씁니다. Rank는 차원 수입니다. 3차원도 같은 방식입니다: new int[4, 4, 4]와 cube[x, y, z]. 2차원 배열에 대한 foreach는 모든 요소를 행 순서대로 방문합니다.
가변 배열(배열의 배열)
가변 배열 int[][]은 요소 자체가 배열인 배열이며, 각 안쪽 배열이 자기 길이를 가질 수 있습니다. 행마다 크기가 다른 "고객별 주문" 같은 데이터에 어울립니다.
출력:
Customer 0: 2 orders, 20, 35
Customer 1: 1 orders, 12
Customer 2: 4 orders, 8, 15, 40, 22
True
new int[3][]은 바깥 배열만 만듭니다. 각 행은 null로 시작하며, 행을 대입하기 전에 orders[0][0]을 쓰면 NullReferenceException이 발생합니다. new int[3][4] 문법은 허용되지 않으므로 각 행을 직접 만들어야 합니다.
진짜 격자(보드, 행렬, 픽셀)에는 [,]를, 행의 길이가 다르거나 행 전체를 바꿔 끼우고 싶을 때는 [][]를 쓰세요.
배열과 List
배열 T[] | List<T> | |
|---|---|---|
| 크기 | 만들 때 고정 | 늘어나고 줄어듦 |
| 개수 | Length 속성 | Count 속성 |
| 추가 또는 제거 | 불가능(Array.Resize뿐) | Add, Insert, Remove |
| 다차원 | [,]와 [][] | 리스트의 리스트만 가능 |
| 전형적인 용도 | 정해진 크기, 조회 표, 버퍼 | 바뀌는 컬렉션 |
변환은 어느 쪽이든 호출 한 번입니다: new List<int>(arr)나 arr.ToList(), 그리고 list.ToArray(). List<T>는 늘어날 수 있는 버전입니다.
흔한 실수
- 루프의
i <= arr.Length. 마지막 유효 인덱스는Length - 1이므로IndexOutOfRangeException이 발생합니다. - 배열을 바로 출력하기.
Console.WriteLine(arr)은System.Int32[]를 출력합니다.string.Join을 쓰세요. Array.Sort가 정렬된 배열을 반환하리라 기대하기.void를 반환하고 제자리에서 정렬합니다.=로 복사하기. 참조가 복사됩니다.Clone()이나ToArray()를 쓰세요.- 만들기 전에 가변 배열의 행 쓰기.
new T[n][]의 각 행은 대입하기 전까지null입니다. - 2차원 배열의 행 수에
Length쓰기. 모든 요소를 셉니다.GetLength(0)을 쓰세요.
자주 묻는 질문
C#에서 배열의 길이는 어떻게 구하나요?
Length 속성을 씁니다: scores.Length. 메서드가 아니라 속성이므로 괄호가 없습니다. 2차원 배열에서 Length는 전체 요소 수이며, 행 수는 GetLength(0), 열 수는 GetLength(1)로 구합니다.
C#에서 배열은 어떻게 정렬하나요?
Array.Sort(arr)는 배열을 제자리에서 오름차순으로 정렬합니다. 내림차순은 정렬 뒤에 Array.Reverse(arr)를 호출하거나 비교를 넘기세요: Array.Sort(arr, (a, b) => b.CompareTo(a)). 원본을 유지하고 정렬된 복사본을 얻으려면 LINQ를 쓰세요: arr.OrderBy(x => x).ToArray().
C#에서 2차원 배열과 가변 배열의 차이는 무엇인가요?
2차원 배열 int[,]은 하나의 직사각형 블록입니다. 모든 행의 열 수가 같고 grid[row, col]로 인덱싱합니다. 가변 배열 int[][]은 배열의 배열입니다. 각 행이 자기 길이를 가질 수 있는 별도의 배열이고 rows[i][j]로 인덱싱합니다. 가변 배열의 각 행은 쓰기 전에 만들어야 하며, 그렇지 않으면 null입니다.
C#에서 배열의 크기를 바꿀 수 있나요?
아니요. 배열의 길이는 만들 때 고정됩니다. Array.Resize(ref arr, newSize)는 크기를 바꾸는 것처럼 보이지만, 새 배열을 할당해 요소를 복사하고 변수가 새 배열을 가리키게 합니다. 크기가 자주 바뀐다면 List<T>를 쓰세요.
배열을 출력하면 System.Int32[]가 나오는 이유는 무엇인가요?
Console.WriteLine(arr)은 arr.ToString()을 호출하는데, 배열은 이를 재정의하지 않으므로 타입 이름이 나옵니다. 대신 요소를 하나의 문자열로 합치세요: Console.WriteLine(string.Join(", ", arr)).
C#에서 배열과 List 중 무엇을 써야 하나요?
12개월이나 게임 보드처럼 요소 수가 정해져 있고 고정이라면 배열을 쓰세요. 요소가 시간에 따라 추가되거나 제거된다면 List<T>를 쓰세요. 둘 다 상수 시간에 인덱싱하며, List<T>는 내부적으로 요소를 배열에 저장합니다.