내일배움캠프 2주차 2일 TIL - 배열 null

백흰범·2024년 4월 23일
0
post-custom-banner

오늘 한 일

  • 스파르타 코딩클럽 진도 나가기 (2-4 ~ 3-2)
  • 2주차 숙제 제출하기
  • TIL 쓰는 방법 강의 듣기
  • 배열 null 판정 (오늘 쓸 내용)

어제까지만 해도 분명히 4주차 강의까지 돌파할 생각이었는데 다른 강의 시간과 피로와 이해 이슈 때문에 빠르게 돌파하지 못해서 아쉬웠다. 하지만 시간 아끼겠다고 강의 내용을 있는 그대로 쓰면은 "나만의 TIL"이 아닌 것 같아서 오늘은 숙제를 풀면서 호기심을 가지게 된 배열 null 판정에 대해서 알아보자

배열 null 판정

왜 이 문제에 대해서 파헤쳐 봤나요?

숙제를 풀고 있는 중에서...

  • 승리 조건문을 짜고 있었는데 배열이 비어있으면 발동시키지 않겠다는 목적으로 이와 같은 코드를 짰었다.
// Main 내에 있는 코드
	string[,] tictactoe = new string[3, 3];   // tictactoe 본체

	if (matchEnd = pg.CheckVictory(tictactoe) && tictactoe != null)	// 배열이 비지 않으면서 승리조건에 충족 시

// Program Class내 메서드
    bool CheckVictory(string[,] board)  // 승리 조건 확인 메서드
 {
     for (int i = 0; i < 3; i++)
     {
         if (board[i, 0] == board[i, 1] && board[i, 1] == board[i, 2])     // 가로승  // 빈칸승 방지
         {
             return true;
         }
         else if (board[0, i] == board[1, i] && board[1, i] == board[2, i])    // 세로승
         {
             return true;
         }
     }

     if (board[0, 0] == board[1, 1] && board[1, 1] == board[2, 2])    // 대각선 1
     {
         return true;
     }
     else if (board[0, 2] == board[1, 1] && board[1, 1] == board[2, 0]) // 대각선 2
     {
         return true;
     }
     else
     {
         return false;	// 아무 승리 조건도 없다면 승리 X
     }
 }
	while (게임 승리까지 반복되는 조건)
    {
    	보드보여주는메서드();
        if (matchEnd = pg.CheckVictory(tictactoe) && tictactoe != null) //승리 조건에 충족되면서 배열이 비지 않았을 경우 조건 코드 실행
        {
                    string whoWin = (oTurn) ? "O" : "X";	// 삼항 연산자
                    Console.WriteLine("===================================");
                    Console.WriteLine($"{whoWin}(가)이 승리했습니다! 축하드립니다~");
                    Console.WriteLine("===================================");
        }
     
        ... (칸을 고르고 지금 턴은 누구이고, 순서 바꾸고 등등의 코드들)
    }	
    // 처음에는 위와 같은 순서로 이루어졌었다. (숙제에서는 위 코드랑 다르게 수정의 수정을 거쳐서 제출했다)

이와 같이 입력했더니 결과..

  • 아무것도 넣지 않았음에도 불구하고 갑자기 승리를 하게 되었다.

위 코드의 문제점

  • 사실 배열 != null은 넘어간다 쳐도 그때의 상식적으로 생각을 해본다면, 배열에 뭐라도 하나 넣으면 당연히 null이 아니게 되게 되니까 소용이 없다.

수정사항

 bool CheckVictory(string[,] board)  //승리 확인 메서드
 {
     for (int i = 0; i < 3; i++)
     {
         if (board[i, 0] == board[i, 1] && board[i, 1] == board[i, 2] && board[i,0] != null)     // 가로승  // 빈칸승 방지
         {
             return true;
         }
         else if (board[0, i] == board[1, i] && board[1, i] == board[2, i] && board[0,i] != null)    // 세로승
         {
             return true;
         }
     }

     if (board[0, 0] == board[1, 1] && board[1, 1] == board[2, 2] && board[0, 0] != null)    // 대각선 1
     {
         return true;
     }
     else if (board[0, 2] == board[1, 1] && board[1, 1] == board[2, 0] && board[0, 2] != null) // 대각선 2
     {
         return true;
     }
     else
     {
         return false;
     }
 }
  • 그냥 승리 조건 메서드에서 승리 조건에 포함되는 칸에 null인지 확인하는 조건을 추가했다.

결과

  • 이렇게하니까 정상적으로 작동이 된다.



배열 null 테스트를 해보는 과정

문제 생각해보기

  • 배열이 비어있다는 기준은 대체 뭘까?

실험실 조성하기

  • 정답을 확실히 알아보려면 직접 코드를 직접 치는 것이 좋을 거라고 생각한다.

테스트해볼 배열들

    internal class Program
    {
    	// 1차원
        static string[] emptyarray;	
        public int[] emptyintarray;
        // 2차원
        private string[,] emptydimension;
        public static int[,] emptyintdimension;
        // 위와 같은 키워드가 된 이유는 키워드에 따라서 초기화가 어떻게 되는지 확인 용도로 했다.

        static void Main(string[] args)
        {
			// 1차원
            string[] array = new string[3];	// string 배열
            int[] intarray = new int[3];	// int 배열
            string[] defaultarray = default(string[]);	// default 함수 활용
			//2 차원
            string[,] dimension = new string[3, 3];
            int[,] intdimension = new int[3, 3];
            string[,] defaultdimension = default(string[,]);

비교를 도와줄 메서드

// 기본 형태 (다른 매개변수 받을 때를 대비해서 오버로딩을 미리 해뒀으니 걱정하지 말자)
static void CheckNull(string[] a, string b)
{
    Console.WriteLine($"{b} {a}");
    if (a == null)
    {
        Console.WriteLine($"{b}는 null이 맞습니다.");
    }
    else
    {
        Console.WriteLine($"{b}는 null이 아닙니다.");
    }

    if (a[0] == null)
    {
        Console.WriteLine($"{b}[0] : {a[0]}는 null이 맞습니다.");
    }
    else
    {
        Console.WriteLine($"{b}[0] : {a[0]}는 null이 아닙니다.");
    }
    Console.WriteLine();
}

// 호출 방법
CheckNull(array, nameof(array));	//배열 데이터와 그걸 가지는 식별자를 인자로 주기로 했다.

테스트 순서

            Program pg = new Program();

            CheckNull(array, nameof(array));						// new string[3]
            CheckNull(intarray, nameof(intarray));					// new int[3]
            CheckNull(defaultarray, nameof(defaultarray));			// default(string[])
            CheckNull(emptyarray, nameof(emptyarray));				// 클래스 멤버 할당 X
            CheckNull(pg.emptyintarray, nameof(emptyintarray));		// 클래스 멤버 할당 X int

            CheckNull(dimension, nameof(dimension));				// new string[3, 3]
            CheckNull(intdimension, nameof(intdimension));			// new int[3, 3]
            CheckNull(defaultdimension, nameof(defaultdimension));	// default(string[,])
            CheckNull(pg.emptydimension, nameof(emptydimension));	// 클래스 멤버 할당 X
            CheckNull(emptyintdimension, nameof(emptyintdimension));// 클래스 멤버 할당 X int

결과?

  • 배열이 null이면 인덱싱에서 막혀버린다. (사실 당연한 걸지도;) (*NullReferenceException)
  • *NullReferenceException = 참조가 null인 타입의 멤버를 접근하려고 하면 발생하는 예외이다.
    NullReferenceException 클래스
    *Reference = 참조
    *Exception = 예외

보완점

if (a == null)
{
    Console.WriteLine($"{b}는 null이 맞습니다.");
    Console.WriteLine();
    return;	// 배열이 null이 맞다면 탈출하기로 했다.
}

결과

  • 수정하니까 막힘 없이 결과가 잘 나왔다.

테스트를 토대로 생각한 나의 답

  • (배열 == null)이 되는 조건

    • 배열에 아무것도 할당하지 않으면 null이다. string[] emptyarray;
    • 배열 할당에서 default(string[])을 활용하려 넣으면 null이 된다. = default(string[]);

default 함수?

  • 사실 default 함수를 호버링 해보면 null 리터럴 또는 가능한 null값을 null을 허용하지 않는 형식으로 변환 시도하려 한다.

  • 이는 int도 동일하다
이를 코파일럿에게 물어본 나


  • default 함수의 정상 작동 모습

  • (위는 컴파일러 결과이다. int의 default 0)
  • (배열 != null)이 되는 조건

    • 배열에 new datatype[]을 넣기라도 한다면 null이 아니게 된다. = new string[3]; || = new int[3];
  • (배열[idx] == null)이 되는 조건

    • 배열에 new string[]을 할당한다면 null이 된다. = new string[3];
      이유를 생각해보면 string 자료형의 기본값이 null이기 때문이다.
  • (배열[idx] != null)이 되는 조건

    • 배열에 new int[]를 할당한다면 null이 아니다. = new int[3];
      int의 기본값은 0이다.



위 과정을 거치면서 알게된 점

참조 타입

코파일럿이 흩어져있는 정보를 최대한 모아주는 덕분에 정보 찾기가 수월하다. (물론 직접 여러 사이트에서 검증하는 과정을 거치는 것이 정확성이 더 높다)


void Method(var variableName) 은 불가능하다.

물론 컴파일러가 직접적으로 알려주는 게 제일 정확하다.


기본값 자동 할당 범위

지역변수에선

  • 지역 변수에서는 기본값 자동 할당이 안된다.

클래스 필드에선

  • 지역 함수 밖에서 가져온다면 키워드와 관계없이 자동 할당이 되는 것 같다.

이를 바탕으로 세운 나의 이론

내 생각에는 해당 지역에 선언된 할당되지 않은 변수는 사용이 불가능하지만
지역 외부에 있는 변수는 할당하지 않아도 사용이 가능하다

코파일럿에게 물어본 결과

  • 모든 변수들은 사용하기 전에 반드시 초기화해야한다.
  • 지역 내에서 할당되지 않은 선언 변수를 사용하려고 하면 컴파일러 오류가 발생한다.
  • 클래스의 필드는 자동으로 기본값으로 초기화된다.



오늘 TIL을 작성하면서 느낀점

오늘 내가 어떤 걸 하겠다라는 목표를 세웠을 때,

기능 구현이나 뭔가를 만들겠다에 관한 목표는 시간 소모량 적은 반면에,
(틱택토 숙제풀이는 1시간이 걸렸다 18시 ~ 19시 [저녁 먹으면서 풀었다])

모르는 것에 대해서, 호기심이 있는 것에 대해서 이해해보겠다는 목표는 시간을 많이 소모하게되었다.
(완전히 배열 비교도 아니고 배열의 null 판정에 대해서 알아보는데 3시간 반이 걸렸다 22시~01시 반)

기능 구현이나 추상적이지 않는 목표들과 같이 무엇이 성공 조건이고 실패 조건인지 확실한 목표에 대해서는 무엇을 해야할 지 뚜렷하고 이미 알고 있는 정보들을 바탕으로 조합해 나가는 방식이라 기획과 절차를 잘 짜두기만 한다면 빠르게 돌파할 수 있는 반면에

무엇에 대해서 알아보겠다 뭔가를 이해하겠다라는 목표무엇이 성공 조건인지 실패 조건인지조차 이해하기 전까지는 알 수가 없는 목표인지라 구체적인 목표를 행하는 것보다도 많은 시간을 허비할 수도 있고, 심지어 지금 이해한 것도 완전히 이해한건가?라는 현재의 의문점을 다시 재귀 호출하는 바람에 또 다시 이해를 하기 위한 과정을 거치게 되는 무한 반복에 빠지게 된다. (그래서 배운 내용들을 일단 있는대로 다 받아적은 뒤에 다시 돌아보면서 이해하는 방식을 채용하는구나를 이해하게 되었다.)
[처음에는 배열 비교라고 작성하려다가 아닌 것 같아서 배열 null으로 바꿨다]

그래서 이해한다라는 것이 이처럼 비효율적이라는 점에서 우리가 눈에 보이는 결과물에 혹하게되는 것이 당연한 흐름일 수도 있지만, 이해하지 못한 코드를 그대로 사용한다면 코드 작성에 큰 변동성을 낼 수가 없기에 기존의 게임들과의 큰 차별성을 가질 수가 없다.

비록 이해한다는 것이나 배운다는 것에 기하급수적인 시간을 소모한다하더라도 기존의 게임들과 차별성 있는 게임을 만들려면 즉, 게이머 분들에게 "난 다른 게임과는 전혀 달라!"라는 게임 매커니즘을 구현하려면, 내가 작성하는 코드에 대해서 끊임 없이 근원을 파고드는 탐구심은 필수라고 생각한다.




내일 할 일

  • 스파르타 코딩클럽 진도 나가기 (3-2 ~ ???)
  • 3주차 숙제 제출하기
profile
게임 개발 꿈나무
post-custom-banner

0개의 댓글