안녕하세요 틴구입니다.
오늘은 저번에 내주신 함수를 이용한 빙고 과제를 선생님께서 직접 만든 코드를 분석해보는 시간을 갖도록 해봅시다.
#include <iostream>
#include <time.h>
// 빙고판 숫자 1 ~ 25출력
void SetNumber(int* pArray)
{
for (int i = 0; i < 25; ++i)
{
pArray[i] = i + 1;
}
}
// 빙고판 숫자 섞기
void Shuffle(int* pArray)
{
for (int i = 0; i < 100; ++i)
{
int Index1 = rand() % 25;
int Index2 = rand() % 25;
int Temp = pArray[Index1];
pArray[Index1] = pArray[Index2];
pArray[Index2] = Temp;
}
}
// SetNumber와 Shuffle를 한 번에 묶기
void Init(int* pPlayer, int* pAI)
{
SetNumber(pPlayer);
SetNumber(pAI);
Shuffle(pPlayer);
Shuffle(pAI);
}
// 빙고판 출력
void OutputNumber(int* pArray, const char* pName, int BingoCount)
{
std::cout << "====================== " << pName << " ======================" << std::endl << std::endl;
for (int i = 0; i < 5; ++i)
{
for (int j = 0; j < 5; ++j)
{
// INT_MAX 는 int타입으로 표현할 수 있는 최대값이 상수로 정해져 있는 것이다.
if (pArray[i * 5 + j] == INT_MAX)
std::cout << "*\t";
else
std::cout << pArray[i * 5 + j] << "\t";
}
// 가로 한줄이 출력이 완료되었다면 개행한다.
std::cout << std::endl;
}
std::cout << pName << " Bingo : " << BingoCount << std::endl;
std::cout << std::endl;
}
// 별 찾기
// bool변수를 이용한 함수사용
bool ChangeNumber(int* pArray, int Number)
{
for (int i = 0; i < 25; ++i)
{
if (pArray[i] == Number)
{
pArray[i] = INT_MAX;
return true;
}
}
// 여기 온 경우라면 이미 입력한 숫자를 다시 중복해서 입력한 것이므로
// false를 리턴한다.
return false;
}
// AI입력
int SelectAI(int* pAI)
{
int SelectNumber[25] = {};
int SelectCount = 0;
// AI가 하나 선택한다. 단, 현재 입력이 안된 숫자중 하나를 랜덤하게 선택해야 한다.
// 선택을 위해서 *이 아닌 일반 숫자들을 배열에 넣어준다.
SelectCount = 0;
for (int i = 0; i < 25; ++i)
{
if (pAI[i] != INT_MAX)
{
SelectNumber[SelectCount] = pAI[i];
++SelectCount;
}
}
// 여기로 빠져나오면 SelectCount는 SelectNumber 배열에 채워준 값의 개수가 된다.
int RandIndex = rand() % SelectCount;
return SelectNumber[RandIndex];
}
int CheckBingoCount(int* pArray)
{
// 빙고 줄 수를 체크한다.
// 가로 줄을 체크한다.
int Check1 = 0, Check2 = 0;
// 체크 전에 빙고 수를 0으로 초기화 하고 모든 줄에 대해서 줄수를 다시 체크해주도록 한다.
int Bingo = 0;
for (int i = 0; i < 5; ++i)
{
// 한줄 체크하기 전에 0으로 초기화를 해주고 그 줄을 체크하도록 한다.
Check1 = 0;
// Check2는 세로 줄을 체크하기 위한 변수이다.
Check2 = 0;
for (int j = 0; j < 5; ++j)
{
if (pArray[i * 5 + j] == INT_MAX)
++Check1;
if (pArray[j * 5 + i] == INT_MAX)
++Check2;
}
// 한 줄이 끝났을 경우 위에서 체크해놓은 Check1이 5라면 그 줄은 모두 *로 변환된 줄이다.
if (Check1 == 5)
++Bingo;
if (Check2 == 5)
++Bingo;
}
// 대각선 체크
Check1 = 0;
for (int i = 0; i < 25; i += 6)
{
if (pArray[i] == INT_MAX)
++Check1;
}
if (Check1 == 5)
++Bingo;
Check1 = 0;
for (int i = 4; i <= 20; i += 4)
{
if (pArray[i] == INT_MAX)
++Check1;
}
if (Check1 == 5)
++Bingo;
return Bingo;
}
void Run(int* pPlayer, int* pAI)
{
int Bingo = 0, AIBingo = 0;
while (true)
{
system("cls");
// 플레이어 번호판 출력
OutputNumber(pPlayer, "Player", Bingo);
// AI 번호판 출력
OutputNumber(pAI, "AI", AIBingo);
if (Bingo >= 5)
{
std::cout << "Player 승리" << std::endl;
break;
}
else if (AIBingo >= 5)
{
std::cout << "AI 승리" << std::endl;
break;
}
int Input = 0;
std::cout << "Input Number(0 : Exit) : ";
std::cin >> Input;
if (Input == 0)
break;
// 1 ~ 25 사이의 숫자가 아니라면 다시 입력하게 한다.
else if (Input < 0 || Input > 25)
continue;
// ChangeNumber 함수의 리턴값이 false라면 이미 입력된 숫자를 다시 입력한
// 것이므로 숫자 자체를 다시 입력받도록 한다.
if (ChangeNumber(pPlayer, Input) == false)
continue;
ChangeNumber(pAI, Input);
Input = SelectAI(pAI);
std::cout << "AISelect : " << Input << std::endl;
// AI가 선택한 숫자로 플레이어의 숫자중 찾아서 *로 바꿔준다.
ChangeNumber(pPlayer, Input);
ChangeNumber(pAI, Input);
Bingo = CheckBingoCount(pPlayer);
AIBingo = CheckBingoCount(pAI);
}
}
int main()
{
srand((unsigned int)time(0));
rand();
int Number[25] = {};
int AINumber[25] = {};
Init(Number, AINumber);
Run(Number, AINumber);
return 0;
}
이렇게 코드를 구성하셨다. main함수가 엄청 깔끔한 것을 확인할 수 있습니다...
신기하네요..
함수 나머지
함수는 선언 부분과 구현 부분으로 나눌 수 있습니다.
void OutputNum(int Number);
int main()
{
int Number = 10;
OutputNum(Number);
return 0;
}
void OutputNumber(int Number)
{
std::cout << Number << std::endl;
}
이런식으로요!!
그리고 함수는 호출하게 되면 해당 메모리로 이동하여 코드를 동작시키고 다시 main코드 블록으로 돌아오는 방식으로 동작이 됩니다.
함수의 이름 자체가 해당 함수의 메모리 주소이기 때문입니다.
함수 포인터는 이런 함수의 주소를 저장할 수 있는 포인터 변수입니다.
지역변수
지연변수란 특정 코드 블록 안에 선언되는 변수를 지역변수라고 합니다.
함수의 인자도 지역변수에 속합니다.
지역변수는 해당 지역을 벗어나게 될 경우 사용할 수 없습니다.
해당 지역에 진입하는 순간 다시 사용할 수 있게 되고 이전에 사용되던 값은 유지가 안됩니다.
ex)
int main()
{
{
int Number5 = 300;
}
// 코드블록 밖이라서 위의 해당 지역변수는 아래의 코드로 출력이 안된다.
std::cout << "Number5 : " << Number5 << std::endl;
return 0;
}
이렇게 main함수 코드블록에 새로운 코드블록을 생성해 지역변수를 만들고 그 밖에서 값을 출력받으려 하면 값을 불러올 수 없다.
전역변수
전역변수란 코드블록 바깥에 선언되는 변수를 전역변수라고 합니다.
전역변수는 해당 변수를 선언한 곳 아래의 모든 코드에서 사용할 수 있게 되고
프로그램이 시작될 때 메모리에 공간이 만들어지고 프로그램이 종료될 때 메모리에서 공간이 제거됩니다.
프로그램이 계속 실행되는 동안에는 같은 값을 유지하면서 어느 곳에서든 사용할 수 있게 됩니다.
ex)
#include <iostream>
// 전역변수의 경우 가독성을 위해 변수 이름 앞에 g_를 붙여서 사용합시다!
int g_Number1 = 100;
void Test()
{
int Number1 = 111;
std::cout << "Number1 : " << Number1 << std::endl;
Number1 += 300;
std::cout << "g_Number1 : " << g_Number1 << std::endl;
g_Number1 += 100;
}
int main()
{
Test();
Test();
Test();
return 0;
}
을 출력하게 되면
Number1 : 111
g_Number1 : 100
Number1 : 111
g_Number1 : 200
Number1 : 111
g_Number1 : 300 이 출력됩니다.
int Number1의 값은 111 그대로 출력되는 반면 정적 변수의 g_Number1는 계속 100씩 증가되는 것을 볼 수 있다.
위에서 설명했듯이 정적변수는 프로그램 종료 전까지 메모리가 유지되기 때문에 계속해서 증감을 진행하는데
Number 1 같은 경우는 매번 함수에 진입할 때마다 초기화를 진행하고 함수가 종료되면 그 값이 제거되기 때문에
계속해서 111이 고정인 것을 확인할 수 있습니다.
디폴트인자
함수의 디폴트인자란 기본값을 설정해둡니다. 만약 인자를 안 넣어주면 기본값으로 설정이 되고 넣어주면 넣어준 값으로 설정이 되는 개념입니다.
ex)
void OutputDefault(int Number = 300)
{
std::cout << Number << std::endl;
}
단 주의해야 할 점이 있습니다.
아래의 코드처럼 함수 오버로딩을 이용할 경우 조심해야 합니다.
// 아래의 코드는 인자가 0 ~ 1개이고
void OutputDefault(int Number = 300)
{
std::cout << Number << std::endl;
}
//디폴트 인자는 가장 오른쪽 인자부터 지정할 수 있다.
// 아래의 코드는 인자가 1 ~ 2개이다
void OutputDefault(int Number, int Number2 = 400)
{
}
이렇게 설정을 하게 되면 인자가 1개로 겹치는 부분이 생긴다면 오류가 일어날 수 있다.
정적변수
정적변수란 static키워드를 이용해서 정적변수를 만들어줄 수 있습니다.
지역변수를 선언할 때 변수 타입 앞에 static키워드를 붙여주면 정적변수가 됩니다.
정적변수는 전역변수처럼 프로그램이 종료될때까지 메모리가 그대로 유지가 됩니다.
정적변수의 초기화는 처음 딱 1번만 진행이 되고 다음부터는 초기화 코드가 무시됩니다.
정적변수도 코드 블록 내에 지역변수로 선언하면 해당 코드블록 외부에서는 사용이 불가능합니다.
#include <iostream>
void Test()
{
int Number1 = 111;
std::cout << "Number1 : " << Number1 << std::endl;
Number1 += 300;
static int Number2 = 100;
std::cout << "Number3 : " << Number2 << std::endl;
Number2 += 100;
}
int main()
{
Test();
Test();
Test();
return 0;
}
이렇게 출력을 하면
Number1 : 111
Number2 : 100
Number1 : 111
Number2 : 200
Number1 : 111
Number2 : 300
이렇게 출력되는 것을 확인할 수 있다.
int Number1의 값은 111 그대로 출력되는 반면 정적변수의 Number2는 계속 100씩 증가되는 것을 볼 수 있다.
위에서 설명했듯이 정적변수도 프로그램 종료 전까지 메모리가 유지되기 때문에 계속해서 증감을 진행하는데
Number 1 같은 경우는 매번 함수에 진입할 때마다 초기화를 진행하고 함수가 종료되면 그 값이 제거되기 때문에
계속해서 111이 고정인 것을 확인할 수 있습니다.
'c++' 카테고리의 다른 글
20210722 객체 지향 프로그래밍 ~ static멤버함수,싱글톤 (0) | 2021.07.23 |
---|---|
20210721 함수 ~ 오퍼레이 (0) | 2021.07.21 |
20210719 포인터,함수 ~ 과제 (0) | 2021.07.20 |
20210716 구조체 ~ 포인터1 (0) | 2021.07.17 |
20210715 빙고 완전체 코드 분석! (0) | 2021.07.15 |
댓글