여태 배운 전처리문은 '#include' 와 주석.
define은 코드치환 + 전처리 정의
define문으로 코드를 간단하게 치환할 수 있다. 아무 것도 없는 것을 치환할 수도 있고, define된 것을 다시 치환할 수도 있다.
#define TEN 10
int main(){
int Value = TEN; // 10을 넣은 것과 같음
}
10;
으로 치환되기 때문에 세미콜론;
을 안 붙여도 오류가 나지 않는다.#define TEN 10;
int main(){
int Value = TEN
}
#define PLUS(Value) Value + Value
int main()
{
int Value = PLUS(20); // 40
}
권장되진 않는다.
너무 쌩치환임.
#define PLUS(Value) Value + Value
#define MUL(Value) Value * Value
int main()
{
int Value = MUL(PLUS(20));
}
여기서 Value의 값은 1600이 아니다.
MUL(PLUS(20))
= MUL(20 + 20)
= 20 + 20 * 20 + 20
= 20 + 400 + 20
괄호가 없고 코드가 쌩으로 치환되는 것이기 때문에 440이 나온다.
헤더 : assert.h
프로그램을 파괴할 수 있는 함수이다.
일부러 심각한 익셉션을 일으켜서 절대로 벌어지면 안 되는 사용법이 사용됐다는 것을 알려주는 것이다.
위에 두 코드를 하나의 define으로 묶어 사용 가능하다.
#include<Windows.h>
#include<assert.h>
#define MsgAssert(TEXT) MessageBoxA(nullptr, TEXT, "치명적 에러", MB_OK); assert(false); // 프로그램을 파괴할 수 있는 함수 Assert
int main()
{
MsgAssert("화면 크기가 0입니다.");
return 0;
}
F5를 눌렀을 때 중단점에 걸리거나 치명적인 오류가 났다면
디버그 탭 > 창 > 호출 스택
에서 호출 스택 창을 켤 수 있다.
현재 호출 스택에서 뒤에 있는 스택을 선택함으로써 지금 에러가 난 부분이나 중단점이 걸린 부분 이전에 함수의 상태를 확인할 수 있다.
_getch()
는 입력 버퍼에 키가 차있을 때까지 기다리는 함수이고, 입력되면 입력 버퍼에서 1개 빼서 리턴해주는 함수이다.
키를 여러 번 눌러서 버퍼에 쌓여있으면 입력을 기다리지 않고 하나씩 리턴된다. 비어있으면 기다리는거임.
그래서 실시간으로 입력받아 움직이는 게임을 만들때는 _getch()로 입력을 기다릴 필요가 없다.
(입력할 때까지 멈춰있는 게임은 없음)
키 입력할 때까지 기다리는 함수가 아닌
입력했는지 안했는지만 보는 함수가 필요하다.
→_kbhit()
int InputCount = _kbhit();
if (InputCount == 0) {
// 입력이 없었으면 바로 return;
return;
}
// 입력이 있었으면 switch문으로 들어감
int Select = _getch();
switch (Select){
...
}
코드 이해하고 메모리맵 그려보기
#include <iostream>
#include <ConsoleEngine/ConsoleScreen.h>
int main()
{
ConsoleScreen NewScreen = ConsoleScreen();
NewScreen.CreateScreen(2, 2);
}
#pragma once
#include "EngineDebug.h"
class ConsoleScreen
{
public:
void CreateScreen(int _ScreenX, int _ScreenY);
protected:
private:
int ScreenX = -1; // 아직 화면이 만들어지지 않은 것을 표현하기 위해 음수
int ScreenY = -1;
char* ScreenData = nullptr; // 내가 원하는 순간 생성을 할 수 있다
};
#include "ConsoleScreen.h"
#include <iostream>
#include <Windows.h>
#include <assert.h>
void ConsoleScreen::CreateScreen(int _ScreenX, int _ScreenY) {
if (_ScreenX <= 0) {
MsgBoxAssert("스크린 X크기가 0이기 때문에 콘솔 스크린을 만들 수 없습니다.");
}
if (_ScreenY <= 0) {
MsgBoxAssert("스크린 Y크기가 0이기 때문에 콘솔 스크린을 만들 수 없습니다.");
}
ScreenX = _ScreenX;
ScreenY = _ScreenY;
int ArrayCount = (ScreenX + 1) * ScreenY + 1;
ScreenData = new char[ArrayCount] {};
for (int y = 0; y < ScreenY; y++) {
for (int x = 0; x < ScreenX; x++) {
int Index = y * (ScreenX + 1) + x;
ScreenData[Index] = '*';
}
int ReturnIndex = y * (ScreenX + 1) + ScreenX;
ScreenData[ReturnIndex] = '\n';
}
printf_s(ScreenData);
}
2 X 2 스크린일 때 출력하면
[*] [*] [\n]
[*] [*] [\n] [0] 가 되어야 함.
1차원이라 아래의 모양이 맞다.
[*] [*] [\n] [*] [*] [\n] [0]
ScreenData가 1차원 배열이기 때문에 개행문자 포함해서
"맵의 행 개수 * (열 개수 + 1) + (0이들어갈자리)" 가 필요한 것이다.
매번 Index = y * (ScreenX + 1) + x;
에 *을 넣는 이유는
y행의 x자리에 별을 넣어야 하는데,
1차원이라 행을 구분하려면 ScreenX크기만큼 곱해서 행을 알아내야 하기 때문이다.
int ReturnIndex = y * (ScreenX + 1) + ScreenX;
는 한 행 끝날 때마다 개행문자를 넣어줘야 해서 마지막 x자리인 ScreenX를 더하는 것이다.
ScreenX = 2, ScreenY = 2
ArrayCount = (2 + 1) * 2 + 1
> y for문 0
y = 0, x = 0
Index = 0 * (2 + 1) + 0 = 0
[*] [0] [0] [0] [0] [0] [0]
, x = 1
Index = 0 * (2 + 1) + 1 = 1
[*] [*] [0] [0] [0] [0] [0]
x 다 돌고
ReturnIndex = 0 * (2 + 1) + 2 = 2
ReturnIndex에 \n 넣기
[*] [*] [\n] [0] [0] [0] [0]
> y for문 1
y = 1, x = 0
Index = 1 * (2 + 1) + 0 = 3
[*] [*] [\n] [*] [0] [0] [0]
y = 1, x = 1
Index = 1 * (2 + 1) + 1 = 4
[*] [*] [\n] [*] [*] [0] [0]
x 다 돌고
ReturnIndex = 1 * (2 + 1) + 2 = 5
ReturnIndex에 \n 넣기
[*] [*] [\n] [*] [*] [\n] [0]
> for문 끝!
ScreenData[ArrayCount - 1] = 0으로 마지막 칸에 0을 넣어서 문자열의 끝을 나타냄
1. char** ScreenData 생김
[0] [0]
2. for문에서 ScreenData[0] 생김
ScreenData[0]
[*] [*] [\n] [0]
3. for문에서 ScreenData[1] 생김
ScreenData[1]
[*] [*] [\n] [0]
→ ScreenData는 이런 느낌?
char ** ScreenData
[ [*] [*] [\n] [0] ]
[ [*] [*] [\n] [0] ]
#pragma once
#include "EngineDebug.h"
class ConsoleScreen
{
public:
void CreateScreen(int _ScreenX, int _ScreenY);
protected:
private:
int ScreenX = -1; // 아직 화면이 만들어지지 않은 것을 표현하기 위해 음수
int ScreenY = -1;
char** ScreenData= nullptr; // 내가 원하는 순간 생성을 할 수 있다
};
#include "ConsoleScreen.h"
#include <iostream>
#include <Windows.h>
#include <assert.h>
void ConsoleScreen::CreateScreen(int _ScreenX, int _ScreenY) {
if (_ScreenX <= 0) {
MsgBoxAssert("스크린 X크기가 0이기 때문에 콘솔 스크린을 만들 수 없습니다.");
}
if (_ScreenY <= 0) {
MsgBoxAssert("스크린 Y크기가 0이기 때문에 콘솔 스크린을 만들 수 없습니다.");
}
ScreenX = _ScreenX;
ScreenY = _ScreenY;
ScreenData = new char* [ScreenY] {};
for (int y = 0; y < ScreenY; y++) {
ScreenData[y] = new char[ScreenX + 2] {0, };
for (int x = 0; x < ScreenX; x++) {
ScreenData[y][x] = '*';
}
ScreenData[y][ScreenX] = '\n';
}
for (int y = 0; y < ScreenY; y++) {
printf_s(ScreenData[y]);
}
}