- 배열

리문·2022년 4월 14일
0
post-thumbnail

배열 (자료구조)

배열이란 같은 데이터타입의 여러 변수를 하나의 묶음으로 나타낸 것이다.

배열의 데이터타입에 대한 동일한 크기로 메모리상에 연속된 공간이 할당된다.
배열명이 메모리 시작 주소이다.
int nums[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 12};

위와 같이 표현 할 수 있다.

int nums[10];

처럼 초기화하지 않으면, 배열 내부의 값은 0이고 식을 통하여 값을 대입할 수 있다.

cout << nums << endl;
for (int i = 0; i < 10; i++)
{
cout << &(nums[i]) << endl;
}

배열명이 메모리 시작 주소이기 때문에, 배열명을 출력하면 해당 배열의 시작 메모리 주소가 출력된다. &를 이용하여 각각 배열의 메모리 주소를 출력할 수도 있다.

실습.

1. string형 배열변수 5개짜리를 선언하고, 반복문을 통해 학생의 이름을 입력받는다.
입력을 다 받고나서 1 ~ 5 사이의 값을 입력하면 해당학생의 이름이 출력된다.

ex) 1번째 학생의 이름을 입력하세요 :
	2번째 학생의 이름을 입력하세요 :
	3번째 학생의 이름을 입력하세요 :
	4번째 학생의 이름을 입력하세요 :
	5번째 학생의 이름을 입력하세요 :

	1 ~ 5 사이의 번호를 입력하세요 : 3

	3번째 학생의 이름은 ... 입니다.
	
    string형 배열변수를 이용하여 값을 입력받는다.
	string name[5] = { };
	int nameNum;
	for (int i = 0; i < 5; i++)
	{
	cout << i + 1 << "번째 학생의 이름을 입력하세요 : ";
	cin >> name[i];
	cout << endl;
	}
    5명의 학생이름을 입력받기 위하여 for문을 이용하였다.
    배열을 불러오기 위해선 1 ~ 5가아닌 0 ~ 4로 불러와야 한다. 
    그렇게 되면 0번째 부터 출력되므로, i + 1로 출력하였다.
    
	cout << "1 ~ 5 사이의 번호를 입력하세요 : ";
	cin >> nameNum;
	cout << endl;
	cout << nameNum << "번째 학생의 이름은 " << name[nameNum - 1] << "입니다!" << endl;
  앞서 i + 1로 출력한 것과 같이, 입력 받는 번호는 1 ~ 5인데, 출력하기 위해선 name[0 ~ 4] 를 이용해야 하므로, 
  name[nameNum -1]이라고 출력해주었다.
    
	

2차원배열 및 다차원배열

	배열이 (*개념적으로 = 실제로는 1차원으로 배열됨.) 2중으로, 다중으로 구성되어 있는 것.

	1차원 배열 : int student_nums[5];		// 5개의 int
				-> 5명의 학생이 있다.
	
	2차원 배열 : int student_nums[3][5];		// 15개의 int
				-> 3개 반에 각각 5명의 학생이 있다.

	3차원 배열 : int student_nums[4][3][5];		// 60개의 int
				-> 4개 학년에 각각 3개 반이 존재, 각 반에 5명의 학생이 있다.
	
	int student_nums[3][5] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15} };

위와같이 표현 할 수 있다.

	cout << student_nums[0][7] << endl; // [1][2] 

정상적인 사용방법은 아니나, 시작 주소로 부터 떨어져있는 거리를 계산하여 값을 출력할수도 있다.

	
	for (int j = 0; j < 3; j++)
	{
	for (int i = 0; i < 5; i++)
	{
		cout << student_nums[j][i] << endl;
		cout << &(student_nums[j][i]) << endl;
	}
	}
    순서대로 출력하는 코드 (※ 변수이름이 같은 것이 메모리가 같다는 것을 의미하지는 않는다.)

실습.

		구구단의 결과를 저장하는 2차원 배열을 하나 선언하고, 
		이중 for문을 이용해서 배열 변수에 구구단의 결과값을 저장하고,
		두 수를 입력받아서 적합한 결과값을 출력해보자.

		ex)
			(결과 저장 완료)
			몇 단을 볼까요? : _
			몇을 곱할까요? : _

			0 * 0 = 28
            
int gugu[8][9];
int guguDan;
int guguNum;

우선 구구단의 결과를 저장할 2차원 배열과 구구단의 단과 곱할 수의 변수를 선언해준다.

for (guguDan = 0; guguDan < 8; guguDan++)
    {
	for (guguNum = 0; guguNum < 9; guguNum++)
	{
		gugu[guguDan][guguNum] = (guguDan + 2) * (guguNum + 1);
		}

구구단의 결과를 이중 for문을 이용하여 저장한다.

gugu[guguDan][guguNum] = (guguDan + 2) * (guguNum + 1);

단은 2단부터 시작하고, 곱할 수는 1 ~ 9 이므로, 단 변수에는 + 2, 곱 변수에는 + 1을 추가해준다.

구구단의 결과값을 저장했다면, 제대로 저장이 되었는지 출력해보아야 한다.

cout << "몇 단을 볼까요? : "; 
cin >> guguDan;
cout << "몇을 곱할까요? : ";
cin >> guguNum;
cout << guguDan << " * " << guguNum << " = " << gugu[guguDan - 2 ][guguNum - 1];

최초 요구사항대로 출력값과 입력값을 저장한 후에, 불러올 변수와 입력받는 변수가 다르기 때문에
gugu[guguDan - 2 ][guguNum - 1] 와 같이 출력해준다.

위와 같이 정상적인 값이 나오는 것을 확인할 수 있다.

실습.

  1. A ~ Z 키 중에 하나를 정답으로 설정한다.
    유저는 한 개의 키를 입력해서 설정된 값을 맞춘다.
    유저가 입력한 키와 설정된 값이 맞으면 승리, 프로그램 종료
    틀리면 남은 기회를 보여주고 5번 틀리면 정답을 알려주고 게임오버.
  • 틀릴 때마다 정답 알파벳이 입력된 알파벳 앞인지 뒤인지 힌트를 준다.

++ 입력된 값이 대소문자 구분없이 처리될 수 있도록 수정해보자.

srand(time(NULL));
char answerNum;
int correctNum = ((rand() % 26) + 65);
cout << "알파벳 맞추기 게임에 오신것을 환영합니다!" << endl 
<< endl << "정답을 입력해주세요!" << endl << endl;

랜덤한 값을 출력해야하므로, srand 타임변수를 지정해주고, 알파벳을 출력하기 위하여
int correctNum = ((rand() % 26) + 65); 로 정답을 지정해준다.
※아스키 코드로 알파벳 대문자 65 ~ 90 이기때문에.

for (int i = 5; i > 0; i--)
{
	cout << "미리보는 정답 : " << (char)correctNum << endl << endl;
	answerNum = _getch();
	cout << "정답 : " << answerNum << endl << endl;
            기회는 총 5번이므로 for의 변수 i는  반복횟수는 5번으로 지정한다.
            남은 기회를 i - 1로 표현하기 위하여 감소로 표현하여 
            for (int i = 5; i > 0; i--) 와 같이 표현하였다.
       ※입력값을 엔터입력없이 바로 입력하는 명령어 : _getch() 
       -> #include <conio.h>로 호출필요.![](https://velog.velcdn.com/images/fukoyjh/post/9ae7e8ca-8cb6-4ff1-9463-5d6f930aae7d/image.png)


위와 같이 출력되는 것을 확인할 수 있다.

           if (answerNum < 95)
	{
		if (correctNum == answerNum)
		{
			cout << "승리하셨습니다!" << endl;
			break;
		}
		else if (i == 1)
		{
			cout << "GAME OVER... 정답은 " << (char)correctNum << "였습니다." << endl << endl;
		}
		else if (correctNum != answerNum)
		{
			cout << "오답입니다..." << i - 1 << "번 기회 남으셨습니다." << endl << endl;				
             if (answerNum > correctNum)
			{
				cout << "HINT! 정답은 " << answerNum << "보다 앞에 있습니다." << endl << endl;
			}
			else if (answerNum < correctNum)
			{
				cout << "HINT! 정답은 " << answerNum << "보다 뒤에 있습니다." << endl << endl;
			}
		}
	}
    correctNum과 answerNum이 같으면 승리멘트와 함께 종료. 
    i가 1이 되어 모든 기회를 소진하면 게임오버. 
    그리고 correctNum과 answerNum 이 같으면 남은 횟수와 힌트로 정답의 위치를 표현하였다.

-> 승리 멘트 출력

-> 오답 시 남은 횟수와 힌트 출력.

-> 게임 오버 시, 정답과 함께 멘트 출력

	else if (answerNum > 95)
	{
		if (correctNum == (answerNum - 32))
		{
			cout << "승리하셨습니다!" << endl;
			break;
		}
		else if (i == 1)
		{
			cout << "GAME OVER... 정답은 " << (char)correctNum << "였습니다." << endl << endl;
		}
		else if (correctNum != (answerNum - 32))
		{
			cout << "오답입니다..." << i - 1 << "번 기회 남으셨습니다." << endl << endl;
			if ((answerNum - 32) > correctNum)
			{
				cout << "HINT! 정답은 " << answerNum << "보다 앞에 있습니다." << endl << endl;
			}
			else if ((answerNum - 32) < correctNum)
			{
				cout << "HINT! 정답은 " << answerNum << "보다 뒤에 있습니다." << endl << endl;
			}
		}
		cout << endl;
	}
}
위와 동일하게 표현한 코드이며 answerNum이 대,소문자 구분이 없이 작동하도록 answerNum이 95보다 크면 answerNum - 32가 동작하도록 if문을 이용하여 추가하였다.
※아스키 코드로 알파벳 소문자 97 ~ 122 이기 때문에.

-> 대,소문자 구별없이 동작함.

실습.

콘솔용 탈출게임.

        1. 10 * 10 크기의 맵을 구성한다. (2차원 배열)
			#O########
			##########
			##########
			##########
			##########
			##########
			##########
			#######E##
			##########
			##########

		2. 플레이어가 존재한다.(0,0)

		3. 탈출지점이 존재한다.(0,0) 제외 랜덤 설정

		4. WASD 키입력을 통해 플레이어를 이동시킨다.

		5. 플레이어가 탈출지점에 도달하면 "탈출에 성공했습니다!" 프로그램 종료.

		※ 화면갱신 명령어 : system("cls");
int mapNum[10][10];
srand(time(NULL));
int playerX = 0;
int playerY = 0;
int escapeX = (rand() % 10);
int escapeY = (rand() % 10);
int button;	
int upButton = 119;
int downButton = 115;
int leftButton = 97;
int rightButton = 100;

10 * 10의 맵을 표현하기 위한 2차배열과 랜덤변수, 플레이어/탈출구 위치변수와 WASD에 맞는 버튼, 그리고 버튼 입력변수를 초기화 한다.

while(escapeX == 0 && escapeY == 0)
 {
 escapeX = (rand() % 10);
 escapeY = (rand() % 10);
 }

플레이어와 탈출구의 시작 위치가 (0, 0)으로 같으면 안되므로, 위와 같이 while문으로 escapeX가 0일때 탈출구의 위치를 재지정 할 수 있게 해준다.

while(1)
{
	for (int x = 0; x < 10; x++)
	{
		for (int y = 0; y < 10; y++)
		{
			mapNum[x][y] = 35;
			mapNum[playerX][playerY] = 80;
			mapNum[escapeX][escapeY] = 69;						
			cout << (char)mapNum[x][y];
			if (y == 9)
			{
				cout << endl;
			}
		}
	}

탈출 지점 도착전까지 반복해야하므로, while문을 사용하고, 이중 for문으로 맵은 mapNum[x][y] = 35;, 플레이어의 위치는 mapNum[playerX][playerY] = 80;, 탈출구의 위치는 mapNum[escapeX][escapeY] = 69; 로 초기화해준다.
cout << (char)mapNum[x][y];로 맵을 출력해주면

위와 같이 출력이 된다.

	button = _getch();
	system("cls");
	if (button == upButton)
	{
		playerX -= 1;
		if (playerX < 0)
		{
			playerX = 0;
		}
	}
	if (button == downButton)
	{
		playerX += 1;
		if (playerX > 9)
		{
			playerX = 9;
		}
	}
	if (button == leftButton)
	{
		playerY -= 1;
		if (playerY < 0)
		{
			playerY = 0;
		}
	}
	if (button == rightButton)
	{
		playerY += 1;
		if (playerY > 9)
		{
			playerY = 9;
		}
	}

button 변수를 _getch()로 입력 받고, button 변수가 각각 wasd와 일치한다면 움직일 수 있도록 if문으로 제어해준다.
만약 d를 눌러 if (button == rightButton)가 동작한다면, playerY의 값이 += 1이 되어

    이와 같이 오른쪽으로 이동한다.
    mapNum[playerX][playerY] 가 mapNum[playerX][playerY += 1] 되었기 때문에
    mapNum[0][0]의 값은 35(#)이되고, mapNum[0][1]가 80(P)의 값을 가짐으로써 움직인다.
    또한, if (playerY > 9) 에 playerY = 9; 라는 if 문을 추가함으로써 맵 밖으로 P가 나가지 못하게 한다.
		
			
		
    if (mapNum[playerX][playerY] == mapNum[escapeX][escapeY])
	{
		cout << "축하합니다! 승리하셨습니다!";
		break;
	}
}

}

이후 mapNum[playerX][playerY]과 mapNum[escapeX][escapeY] 가 같으면 탈출구에 플레이어가 도착한 것임으로 축하멘트와 break문으로 whlie문을 종료해준다.

처음 이 문제를 해결하려고 했을 때는 맵을 for문 내에서 표현했기 때문에, 이동을 어떻게 표현할지가 가장 난관이었다.
while문으로 입력을 반복해주면서, 이동을 플레이어 위치변수의 값 변환하는 if문으로 작동하는 아이디어를 떠올린 후에 해결할 수 있었다.

아직 부족하지만, 제법 모양이 잡힌 결과물이 나오니 뿌듯하지 아니할수가 없다.
P.S. 저녁도 거르면서 집중한 나를 칭찬한다! But, 오늘 저녁은 맛있는거 먹자.

profile
개발자되기 대작전

0개의 댓글