기존에 대학교에서 C/C++(메인)과 파이썬(서브)로 배웠기 때문에, 기본적인 C#의 문법은 빠르게 스킵하며 넘어갔었다. 그런데, 생각보다 미묘하게 기본 문법을 표현하는 방식이 달라서 아주 자주 쓰는 다차원 배열 표현법에서 내가 착각했던 부분을 정리하고 넘어가려고 한다.
C# 다차원 배열 사용법
- Array 클래스를 사용하기 위해서는 우선 네임스페이스를 선언해야 한다.
//네임스페이스 선언
using System;
- 1차원 배열 사용법
using System;
class ArrayPractice
{
static void Main()
{
//1차원 배열 선언
int[] array1;
//1차원 배열 할당(인스턴스 생성)
array1 = new int[3];
//1차원 배열 초기화
array1[0] = 1;
array1[1] = 6;
array1[2] = 9;
//인덱스를 통한 값 출력
Console.WriteLine($"{array1[0]} {array1[1]} {array1[2]}");
//1차원 배열 선언, 할당, 초기화 한 번에 하기
int[] array2 = new int[3] { 1, 2, 3 };
//위를 줄여서 사용하기
int[] array3 = { 1, 2, 3 };
}
}
//출력 : 1 6 9
- 다차원 배열 사용법
using System;
class ArrayPractice
{
static void Main()
{
//2차원 배열 선언
int[,] array2D_1;
//2차원 배열 할당(인스턴스 생성)
array2D_1 = new int[2, 2];
//2차원 배열 초기화
array2D_1[0, 0] = 1;
array2D_1[0, 1] = 2;
array2D_1[1, 0] = 3;
array2D_1[1, 1] = 4;
//인덱스를 통한 값 출력
Console.WriteLine(@$"
{array2D_1[0, 0]} {array2D_1[0, 1]}
{array2D_1[1, 0]} {array2D_1[1, 1]}
");
//2차원 배열 선언, 할당, 초기화 한 번에 하기
int[,] array2D_2 = new int[2, 2] { {11, 22}, {33, 44} };
//위를 줄여서 사용하기
int[,] array2D_3 = { {11, 22}, {33, 44} };
}
}
//출력 : 1 2 3 4
가독성을 위해 다차원 배열 중 가장 간단한 2차원 배열을 예제로 작성했다. 고정된 길이의 다차원 배열을 사용할 경우, C/C++에서 array[1][2]와 같이 사용하는 것과 달리 C#에서는 array[1, 2]와 같이 사용한다는 차이점이 있다. 다만 혼동하면 안되는 점은 C#에서도 array[1][2]와 같은 표현을 사용한다. 단, 이는 가변 길이의 배열에 대한 접근법이다.
C# 가변 배열 사용법
- 가변 배열 사용법
using System;
class ArrayPractice
{
static void Main()
{
//가변 배열 선언
int[][] vaArray1;
//가변 배열 할당 : 2개의 각 요소는 이후 동적으로 초기화 가능
vaArray1 = new int[2][];
//가변 배열 초기화 : 각 요소에 동적으로 초기화
vaArray1[0] = new int[] { 1, 2 };
vaArray1[1] = new int[] { 11, 22, 33 };
//인덱스를 통한 값 출력
Console.WriteLine($@"
{vaArray1[0][0]} {vaArray1[0][1]}
{vaArray1[1][0]} {vaArray1[1][1]} {vaArray1[1][2]}
");
//가변 배열 선언, 할당, 초기화 한 번에 하기
int[][] vaArray2 = new int[2][] { new int[]{ 1, 2 }, new int[]{ 11, 22, 33 } };
//위를 줄여서 사용하기
int[][] vaArray3 = { new int[]{ 1, 2 }, new int[]{ 11, 22, 33 } };
}
}
//출력 : 1 2 11 22 33
각 행의 길이가 다른 배열을 만들고 싶을 경우, 가변 배열을 사용할 수 있다. 다만 가변 배열의 경우 그 크기가 커질 경우 각 행의 길이가 다르기에 적절하지 못한 인덱스를 참조할 수 있다. 없는 항목을 조회하려고 하면 오류가 발생하거나 문제가 생길 수 있기 때문에, 배열의 속성과 메서드를 활용해 각 행에 대해 길이를 구해 적절히 제어할 수 있다. 가독성을 위해 2차원가변 배열을 다뤘지만 3차원 이상의 가변 배열에 대해서도 동작한다. 그러나 고정 길이 배열과 가변 배열은 기본적으로 구조가 다르기 때문에 속성과 메서드를 사용하고 이를 활용할 때 주의해야 한다.
배열 관련 유용한 속성과 메서드
위의 문제점에 대해 활용할 수 있는 속성과 메서드만을 다룬다. 또한 다차원 배열과 가변 배열에 대해 속성을 조회해보며 주의해야 할 부분에 대해 논한다.
- 속성
- Rank : 배열의 차수
- Length : 배열의 길이
- 메서드
- GetLength() : 입력한 차원에 대한 길이
- 다차원 배열 분석하기
using System;
class ArrayPractice
{
static void Main()
{
//2X2X2 3차원 배열 생성
int[,,] arr3D = { { { 1, 2 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } };
//3차원 배열의 차수 = 3
Console.WriteLine(arr3D.Rank);
//3차원 배열의 총 길이 = 8
Console.WriteLine(arr3D.Length);
//arr[i][][] <- i가 있는 부분의 길이 = 2
//: AXBXC 배열에서 A구하기
Console.WriteLine(arr3D.GetLength(0));
//arr[][i][] <- i가 있는 부분의 길이 = 2
//: AXBXC 배열에서 B구하기
Console.WriteLine(arr3D.GetLength(1));
//arr[][][i] <- i가 있는 부분의 길이 = 2
//: AXBXC 배열에서 C구하기
Console.WriteLine(arr3D.GetLength(2));
}
}
//출력 : 3 8 2 2 2
- 가변 배열 분석하기
using System;
class ArrayPractice
{
static void Main()
{
//가변 배열 생성
int[][] vaArr = { new int[]{1, 2}, new []{3, 4, 5} };
//가변 배열의 차수 = 1
Console.WriteLine(vaArr.Rank);
//가변 배열의 총 길이 = 2
Console.WriteLine(vaArr.Length);
/*
vaArr = {vaArr[0], vaArr[1]}
vaArr[0] ---> {1, 2}
vaArr[1] ---> {3, 4, 5}
따라서 vaArr의 차수는 1, 길이는 2
*/
}
}
//출력 : 1 2
다차원 배열까지는 큰 문제 없이 이해할 수 있다. 가변 배열의 경우 차수는 2, 길이는 5라고 생각할 수 있지만, 가변 배열이 구현된 원리를 파악한다면 차수가 1, 길이가 2가 출력되어야 맞다. 처음 구조가 확정되는 다차원 배열과 달리 가변 배열을 각 요소의 길이가 얼마가 들어올지 알 수 없다. 따라서 가변 배열은 일반적인 배열과 다르게 각 변동적인 배열을 가리키는 포인터 배열을 가진다. 위의 예제처럼 { {1, 2}, { 3, 4, 5} }를 저장하는 경우 5개 항목을 바로 저장하는 게 아니라 먼저 배열에는 {포인터1, 포인터2}로 저장되고, 각 포인터는 {1, 2}와 {3, 4, 5}를 가리키는 방식이다.
따라서 C/C++에서 익숙한 vaArr[0][1]이라는 표현은 0행 1열 항목이 아니라, vaArr[0] 포인터가 가리키는 배열에서 인덱스 1번 항목을 의미하는 것이다. 결과적으로는 비슷하더라도 원리적 차이를 이해할 필요가 있을 듯하다.
'C#' 카테고리의 다른 글
[C#] Action, Func, Predicate 제네릭 대리자(델리게이트; Delegate)와 매개변수에 메서드 전달 (0) | 2024.04.26 |
---|---|
[C#] 대리자(Delegate; 델리게이트)와 무명 메서드(+람다식 기초) (1) | 2024.04.26 |
[C#] 참조 매개 변수, ref와 out의 차이점 (0) | 2024.04.25 |
[C#] 값 형식과 참조 형식, 박싱과 언박싱(힙 메모리), is 연산자와 as 연산자 (0) | 2024.04.25 |
[C#] 널(NULL) 관련 형식[Nullable<T>] 및 연산자[??, ?.] (1) | 2024.04.24 |
[C#] 제네릭(Generic) 클래스 (0) | 2024.04.24 |
[C#] 컬렉션(Collection) 클래스 (0) | 2024.04.24 |
[C#] Rider 내 실습용 콘솔 솔루션 생성 & 유용한 문자열 형식, 표기법 (1) | 2024.04.23 |