실제로 램은 1차원 메모리이기 때문에 램 상에 2차원으로 존재하는 것은 아니고, 문법일 뿐이다.
#include <iostream>
int main()
{
char Arr[3][4] = { {'0', '1', '2'}, {'3', '4', '5'}, {'6', '7', '8'} };
// char Arr[3][4] = { "123", "345", "678" };
// [[0][0]] [[0][1]] [[0][2]] [[0][3]]
// [[1][0]] [[1][1]] [[1][2]] [[1][3]]
// [[2][0]] [[2][1]] [[2][2]] [[2][3]]
// '0' '1' '2' 0
// '3' '4' '5' 0
// '6' '7' '8' 0
__int64 Address = reinterpret_cast<__int64>(&Arr); // 100번지(라고 한다면)
__int64 Address0 = reinterpret_cast<__int64>(&Arr[0][0]); // 100번지
__int64 Address1 = reinterpret_cast<__int64>(&Arr[0][1]); // 101번지
__int64 Address2 = reinterpret_cast<__int64>(&Arr[0][2]); // 102번지
__int64 Address3 = reinterpret_cast<__int64>(&Arr[0][3]); // 103번지
__int64 Address4 = reinterpret_cast<__int64>(&Arr[1][0]); // 104번지
__int64 Address5 = reinterpret_cast<__int64>(&Arr[1][1]); // 105번지
__int64 Address6 = reinterpret_cast<__int64>(&Arr[1][2]); // 106번지
__int64 Address7 = reinterpret_cast<__int64>(&Arr[1][3]); // 107번지
__int64 Address8 = reinterpret_cast<__int64>(&Arr[2][0]); // 108번지
// .....
// Arr char[3][4]형
// [00] [01] [02] [03]
// [10] [11] [12] [13]
// [20] [21] [22] [23]
// Arr[0] char[4]형
// ['0'] ['1'] ['2'] [0] = "012" => Arr[0]
// ['3'] ['4'] ['5'] [0] = "345" => Arr[1]
// ['6'] ['7'] ['8'] [0] = "678" => Arr[2]
// Arr[0][0] char형
int IntArr[10];
int* Ptr = IntArr;
char* Ptr0 = Arr[0];
char* Ptr1 = Arr[1];
char* Ptr2 = Arr[2];
printf_s(Ptr0);
printf_s(Ptr1);
printf_s(Ptr2);
}
#include <iostream>
int main()
{
{
int Var = 10;
Arr[10][Var]; // 불가능!
// 한번 컴파일이 되고 나면, 함수의 실행 메모리는 고정되어 있어야 한다
// exe에 함수가 사용할 메모리의 크기가 적혀 있으므로 절대로 바뀔 수 없다
// 그래서 변수는 배열의 크기를 지정하는 데에 사용할 수 없다
}
{
const int ScreenX = 6;
const int ScreenY = 5;
char Arr[ScreenY][ScreenX] = {0, }; // 가능
// char Arr[5][6];
// 변할 가능성이 없다고 판단해 컴파일러가 상수로 대체해버린다
for (int y = 0; y < ScreenY; y++)
{
for (int x = 0; x < ScreenX - 1; x++)
{
Arr[y][x] = '*';
}
}
// ['*'] ['*'] ['*'] ['*'] ['*'] [0]
// ['*'] ['*'] ['*'] ['*'] ['*'] [0]
// ['*'] ['*'] ['*'] ['*'] ['*'] [0]
// ['*'] ['*'] ['*'] ['*'] ['*'] [0]
// ['*'] ['*'] ['*'] ['*'] ['*'] [0]
for (int y = 0; y < ScreenY; y++)
{
char* Ptr = Arr[y];
printf_s(Ptr);
printf_s("\n");
}
// *****
// *****
// *****
// *****
// *****
}
}
/*
<과제1 : 상하좌우 조작 만들기>
<과제2 : 화면 외곽을 벽으로 만들기>
<과제3 : 플레이어가 특정 키를 누르면 화면에 총알이 나타나도록 만들기>
*/
#include <iostream>
#include <conio.h>
class int2
{
// 보통 수학 관련 클래스는 멤버변수를 public으로 둔다
// 일반적인 수학 함수처럼 접근을 용이하게 하기 위해
public:
int X = 0;
int Y = 0;
};
typedef int2 Pos; // 권장하진... 않음...
// 전역변수는 영역과 관련없이, 위에 있다면 사용 가능하다
const int ScreenX = 21;
const int ScreenY = 10;
const int ScreenXHalf = ScreenX / 2;
const int ScreenYHalf = ScreenY / 2;
class ConsoleScreen
{
public:
ConsoleScreen(char _BaseChar)
{
BaseCh = _BaseChar;
}
// 이미 특정 형태로 생성자를 만들었기 때문에
// ConsoleScreen() 이 디폴트 생성자가 자동으로 생기지 않는다
void PrintScreen()
{
for (int y = 0; y < ScreenY; y++)
{
char* Ptr = Arr[y];
printf_s(Ptr);
printf_s("\n");
}
}
void ClearScreen()
{
system("cls");
for (int y = 0; y < ScreenY; y++) // (2)
{
Arr[y][0] = '#';
Arr[y][ScreenX - 2] = '#';
}
for (int x = 0; x < ScreenX - 1; x++) // (2)
{
Arr[0][x] = '#';
Arr[ScreenY - 1][x] = '#';
}
for (int y = 1; y < ScreenY - 1; y++)
{
for (int x = 1; x < ScreenX - 2; x++)
{
Arr[y][x] = BaseCh;
}
}
}
void SetPixel(const int2& _Position, char _Char)
{
SetPixel(_Position.X, _Position.Y, _Char);
}
void SetPixel(int _X, int _Y, char _Char)
{
Arr[_Y][_X] = _Char;
}
protected:
private:
char Arr[ScreenY][ScreenX] = { 0, };
char BaseCh = ' ';
};
class Player
{
public:
Player()
{
}
Player(const int2& _StartPos, char _RenderChar)
: Pos(_StartPos), RenderChar(_RenderChar)
{
}
inline int2 GetPos() // inline(치환)에 성공하면 조금 더 빨라질 것
{
return Pos;
}
inline char GetRenderChar() // inline(치환)에 성공하면 조금 더 빨라질 것
{
return RenderChar;
}
inline bool GetIsFire() // (3)
{
return IsFire;
}
void CheckPos() // (2)
{
if (Pos.X < 1)
{
Pos.X = 1;
}
else if (Pos.X > ScreenX - 3)
{
Pos.X = ScreenX - 3;
}
else if (Pos.Y < 1)
{
Pos.Y = 1;
}
else if (Pos.Y > ScreenY - 2)
{
Pos.Y = ScreenY - 2;
}
}
void Update()
{
int dir = _getch(); // 리턴값은 꼭 받아서 확인해보자!
switch (dir) // (1)
{
// case를 두개씩 겹처서 만들 수 있다!
case'a':
case'A':
{
Pos.X -= 1;
break;
}
case'd':
case'D':
{
Pos.X += 1;
break;
}
case'w':
case'W':
{
Pos.Y -= 1;
break;
}
case's':
case'S':
{
Pos.Y += 1;
break;
}
case'q': // (3)
case'Q':
{
IsFire = true;
}
default:
break;
}
CheckPos(); // (2)
}
private:
int2 Pos = { 0, 0 };
char RenderChar = '@';
bool IsFire = false;
};
class Bullet // (3)
{
public:
Bullet()
{
}
inline int2 GetPos()
{
return Pos;
}
inline char GetRenderChar()
{
return RenderChar;
}
bool IsVisible(bool _IsFire)
{
Visible = _IsFire;
return Visible;
}
protected:
private:
int2 Pos = { 0, 0 };
char RenderChar = '^';
bool Visible = false;
};
int main()
{
ConsoleScreen NewScreen = ConsoleScreen('*');
Player NewPlayer = Player({ ScreenXHalf, ScreenYHalf }, '@');
Bullet NewBullet = Bullet();
while (true)
{
NewScreen.ClearScreen();
NewScreen.SetPixel(NewPlayer.GetPos(), NewPlayer.GetRenderChar());
if (NewBullet.IsVisible(NewPlayer.GetIsFire())) // (3)
{
NewScreen.SetPixel(NewBullet.GetPos(), NewBullet.GetRenderChar());
}
NewScreen.PrintScreen();
NewPlayer.Update();
}
}
📢 기억해두면 좋은 VS단축키 & 정보
- Shift + Del ⇒ 한 줄 삭제
- Shift + 위/아래 ⇒ 한 줄 드래그
- Shift + Home ⇒ 한 줄 안에서 커서 앞쪽으로 드래그
- Shift + End ⇒ 한 줄 안에서 커서 뒤쪽으로 드래그
- Shift + Ctrl + 좌/우 ⇒ 단어 단위로 드래그
- Shift + Alt + 위/아래 ⇒ 커서 n층으로 만들기
- Shift + Alt + 위/아래 → Ctrl + 좌/우 ⇒ n층 단어 단위로 드래그
- Alt + 드래그 ⇒ 지정 사각형만큼 드래그
- 범위지정 + Alt + 위/아래 ⇒ 윗줄 또는 아랫줄로 이동
- Ctrl + 좌/우 ⇒ 단어 단위로 이동
- Ctrl + A ⇒ 문서 코드 전체 선택
- 변수명 위에 커서 + Ctrl + R + R ⇒ 동일한 개념을 가진 변수명을 동시에 변경
- 항상 제대로 작동하는 것은 아니다
- 변수가 많을수록 엄청 오래 걸린다
- 범위지정 + Ctrl + K+ C ⇒ 주석
- Ctrl + K + F ⇒ 자동 줄맞춤
- 파일명 위에 커서 + Ctrl + Shift + G ⇒ 파일 열기
- 이름 위에 커서 + F12 ⇒ 정의로 이동