우리는 그동안 scanf를 통해 입력을 받았으나 이 함수 외에도 다른 많은 함수들이 있다.
fgetc, getc, getchar 등 다양한 함수들이 있는데 어떤 차이가 있을까?
#include <stdio.h>
int fgetc(FILE* stream);
스트림(stream) 에서 문자 하나를 읽어온다
인자로 전달한 stream의 파일 위치 지정자가 가리키는 문자를 리턴한다. 이 때 파일 위치 지정자는 그 다음 문자를 가리키게 된다.
왜 리턴값이 int인가요?
fgetc
는 읽어들인 문자를 int값으로 리턴하는데 이는 EOF
를 리턴하기 위함이다.
EOF
의 int값은 -1로, 만일 파일의 끝(End-of-File) 에 도달하였거나, 읽는 도중 오류가 발생하였다면 EOF
를 리턴하고, 이에 대응하는 오류 표시자나 파일 끝 표시자가 설정된다.
getc
는 fgetc
와 기능적으로 동일하지만 getc
는 매크로 형태로 구현이 되어있는 반면, fgetc
는 함수 형태로 구현되어 있다.
getc
는 매크로 형태로 구현되어 있어 fgetc
보다 속도가 빠르다는 이점이 있다.
#define getchar getc(File* stream)
int getchar(void)
getchar
함수는 결국 getc
함수를 좀 더 편하게 사용할 수 있도록 만들어진 매크로 함수로, 스트림을 인자로 받지 않는다는 편리성을 지니고 있다.
위 세 함수에 대해 알아보았으니 이제 다음과 같은 질문을 해도 되겠다.
scanf는 왜 getchar()보다 느린 것인가?
scanf
함수는 키보드로 부터 input을 받아 format specifer (출력 타입 %d, %c...)에 저장시킨다. 이때 format specifer variable이 읽을 수 있는지 없는지를 확인하는데 getchar
함수는 그런 과정 없이 바로 읽어 성능이 더 빠르다.
그러나 아직 끝난게 아니다. getche
와 getch
함수가 남았다.
먼저 getch
를 사용하기 위해선 conio.h
란 헤더를 이용하여야 한다.
conio란 console input/output을 뜻한다.
비교 | getchar() | getche() | getch() |
---|---|---|---|
버퍼 사용 | O | X | X |
화면 표시 | O | O | X |
종료 인식 | \n | \r | \r |
getchar
는 버퍼를 사용하고 getch
와 getche
는 버퍼를 사용하지 않는다.
입력을 하면 바로 들어가는게 아니라 입력버퍼
라는 곳에 담긴다. 엔터가 들어올 때 까지 입력을 계속 담아두다가 엔터가 들어오면 입력을 중지하고 지금까지 입력된 내용에서 첫 글자를 리턴한다. 따라서 잘못 입력해도 엔터를 치기 전엔 수정 가능하다.
버퍼를 사용하지 않는 경우는 키보드를 눌렀다가 떼는 동시에 그 값을 가져가 버린다.
getch
의 경우 입력하는 값이 화면에 나타나지 않는다
getche() 의 경우 입력하는 값이 화면에 출력되며 이를 echo 라고 해서 앞자를 딴 e가 붙는다고 한다.
getchar() 또한 화면에 나타난다
각각의 함수가 Enter 값을 인식하는데 차이가 있다.
getchar()의 경우 \n 으로 인식, getch(), getche() 는 \r 인식한다.
int main()
{
Canvas* canvas = new Canvas(80);
Player* player = new Player("(^___^)", 50, true);
Enemy* enemy = new Enemy("(*-*)", 10, true);
const int max_bullets = 5;
Bullet* bullets[max_bullets];
for (int i = 0; i < max_bullets; i++) {
bullets[i] = nullptr;
}
while (true)
{
canvas->clear();
if (_kbhit()) {
Bullet* found;
char input = _getch();
switch (tolower(input)) {
case 'a': player->setPos(player->getPos() - 1);
break;
case 'd': player->setPos(player->getPos() + 1);
break;
case ' ':
found = nullptr;
for (int i = 0; i < max_bullets; i++) {
Bullet* bullet = bullets[i];
if (bullet == nullptr) { //이렇게 하는 것보다 bullets 초기화 단계에서 동적할당을 하는 것이 낫지 않았나?
bullet = new Bullet(">", 0, false, 0, 0);
bullets[i] = bullet;
found = bullet;
break;
}
if (bullet->isVisible() == true) continue;
found = bullet;
break;
}
if (found != nullptr) found->fire(*player, *enemy);
}
}
player->draw(*canvas);
enemy->draw(*canvas);
for (int i = 0; i < max_bullets; i++) {
if (bullets[i] == nullptr) continue;
bullets[i]->draw(*canvas);
}
player->update();
enemy->update();
for (int i = 0; i < max_bullets; i++) {
if (bullets[i] == nullptr) continue;
bullets[i]->update(*player, *enemy);
}
canvas->render();
Sleep(100);
}
for (int i = 0; i < max_bullets; i++) {
delete bullets[i];
}
delete[] bullets;
delete enemy;
delete player;
delete canvas;
return 0;
}
int main()
{
Canvas* canvas = new Canvas(80);
Player* player = new Player("(^___^)", 50, true);
Enemy* enemy = new Enemy("(*-*)", 10, true);
const int max_bullets = 5;
Bullet* bullets[max_bullets];
for (int i = 0; i < max_bullets; i++) {
bullets[i] = new Bullet(">", 0, false, 0, 0);
}
while (true)
{
canvas->clear();
if (_kbhit()) {
Bullet* found;
char input = _getch();
switch (tolower(input)) {
case 'a': player->setPos(player->getPos() - 1);
break;
case 'd': player->setPos(player->getPos() + 1);
break;
case ' ':
for (int i = 0; i < max_bullets; i++) {
if (bullets[i]->isVisible() == false) {
bullets[i]->fire(*player, *enemy);
break;
}
else continue;
}
}
}
player->draw(*canvas);
enemy->draw(*canvas);
for (int i = 0; i < max_bullets; i++) {
if (bullets[i] == nullptr) continue;
bullets[i]->draw(*canvas);
}
player->update();
enemy->update();
for (int i = 0; i < max_bullets; i++) {
if (bullets[i] == nullptr) continue;
bullets[i]->update(*player, *enemy);
}
canvas->render();
Sleep(100);
}
for (int i = 0; i < max_bullets; i++) {
delete bullets[i];
}
delete[] bullets;
delete enemy;
delete player;
delete canvas;
return 0;
}