본문 바로가기
C#

[C#] 다차원 배열과 가변 배열(C/C++ 문법과 차이점)

by RucA 2024. 4. 25.
728x90
반응형

기존에 대학교에서 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번 항목을 의미하는 것이다. 결과적으로는 비슷하더라도 원리적 차이를 이해할 필요가 있을 듯하다.

728x90
반응형