Notice
Recent Posts
Recent Comments
Link
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

문타쿠, 공부하다.

[독하게 시작하는 C 프로그래밍] 섹션 9. 표준 입/출력 본문

C언어/독하게 시작하는 C 프로그래밍

[독하게 시작하는 C 프로그래밍] 섹션 9. 표준 입/출력

개발새발 문타쿠 2023. 10. 28. 23:13

콘솔(Console)이란 무엇일까?

콘솔(Console)

  • 텍스트 기반의 사용자 인터페이스
  • 컴퓨터에서 텍스트로 명령을 입력하고 출력을 확인하는 환경을 의미
  • 콘솔은 여러 OS에서 사용될 수 있으며, 사용자와 컴퓨터 간의 상호작용을 텍스트 형식으로 제공한다.

cmd

  • Windows 운영체제에서 사용하는 CLI(Command Line Interface) 프로그램

Console과 CLI

  • CLI기반 HCI(Human Computer Interface)는 키보드 입력으로 구현
  • 키보드 입력 시 그 값은 메모리의 I/O Buffer에 연속적으로 저장
  • I/O Buffer에서 한 글자 단위로 처리

인터럽트

  • 컴퓨터 = CPU + RAM + 주변기기
  • CPU와 주변기기들은 서로 상호작용/통신을 하며 정보를 주고받는다.
  • 이때 CPU와 주변기기들의 원활한 상호작용/통신을 지원하는 역할을 인터럽트라고 한다.

문자 입/출력

getchar() 함수

  • 여러 개의 문자를 입력해도 첫 번째 문자 즉, 한 글자만 메모리에 저장하는 함수

putchar() 함수

  • getchar() 함수와 대응하는 함수로, 한 글자만 출력하는 함수

getchar() / putchar()

  • Buffered I/O
  • 값이 저장된 메모리의 값을 읽거나 출력
#include <stdio.h>

int main(void)
{
	char ch = getchar();
	putchar(ch);

	printf("\n\n");
	putchar('Y');

	return 0;
}


_getch() / _getche()

  • Non-Buffered I/O
  • 키보드 입력 자체에 대한 감지
  • 함수 사용 시 conio.h 헤더파일 추가하기
  • _getch() 함수는 화면상에 내가 문자를 입력한게 안보이고, _getche() 함수는 보임
#include <stdio.h>
#include <conio.h>

int main(void)
{
	char ch = _getch();
    
	printf("입력한 키는 ");
	putchar(ch);
	printf("입니다.");

	return 0;
}

#include <stdio.h>
#include <conio.h>

int main(void)
{
	char ch = _getche();

	printf("\n입력한 키는 ");
	putchar(ch);
	printf("입니다.");

	return 0;
}


문자열 입/출력

scanf(), scanf_s() / printf()는 아래에서 따로 다루겠슴

 

문자열 입력

  • gets()
    • 보안 결함 문제로 사용하지 말라고 알려져 있음
  • gets_s()
    • 윈도우 계열에서 권장
    • 크기를 명시해야 함
  • fgets()
    • 리눅스, 유닉스 계열에서 권장

문자열 출력

  • puts()
    • 개행문자가 포함된 상태로 실행된다.
#include <stdio.h>

int main(void)
{
	char name[10] = { 0 };

	printf("키미노 나마에와? ");
	gets_s(name, sizeof(name));

	printf("와타시노 나마에와 ");
	puts(name);
	puts("입니다요.");

	return 0;
}


gets() 함수와 보안 결함 (feat. 시큐어 코딩)

gets() 함수의 보안 결함

  • 매개변수로 메모리의 주소를 받지만 얼마나 써도 되는지 크기를 확인하지 않아 발생
  • 메모리 경계를 벗어난 쓰기를 수행
  • 보안 문제가 발생하지 않도록 코드 수준에서 대응하는 것이 중요(시큐어 코딩)

형식 문자와 이스케이프 시퀀스

형식 문자

형식문자 설명 자료형
%d 정수 int
%o 8진수 정수
%x, %X 16진수 정수
%u 부호 없는 정수 unsigned int
%f 부동 소수점 float, double
%e, %E, &g, &G 부동 소수점 과학적 표기
%c 문자 char, int
%s 문자열 char*
%p 포인터 포인터 자료형
%zd 크기 size_t
등등

이스케이프 시퀀스

문자 의미 문자 의미
\a 경고음 \\ \
\b Backspace \? ?
\f Form feed \' '
\n New line \" "
\r Carriage return \ooo 8진수
\t Tab \xhh 16진수
\v Vertical tab 등등

실수 출력

단정도 float 말고 배정도 double 사용하기


문자, 정수 입력과 개행문자 제거

scanf_s()로 문자를 입력받을 때

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char array[5] = { 0 };

	// %5c: char를 5번 읽어서 배열에 집어 넣겠다는 뜻
	// _countof(array): sizeof()가 크기라면 _countof()는 개수를 의미
	// 공백문자도 입력 값으로 인식함
	scanf_s("%5c", array, _countof(array));
	printf("%c, %c, %c, %c, %c\n", \
		array[0], array[1], array[2], array[3], array[4]);

	return 0;
}


scanf_s()로 연속된 값을 입력받을 때 어떤 형식 지정자를 사용하든 형식 지정자 사이에는 공백이 음슴 주의

#include <stdio.h>

int main(void)
{
	int num1 = 0;
	int num2 = 0;
	int num3 = 0;

	// 연속된 값을 입력받을 때 형식 지정자 사이에 공백 ㄴㄴ
	scanf_s("%d%d%d", &num1, &num2, &num3);
	printf("입력한 숫자는 %d, %d, %d입니다.", num1, num2, num3);

	return 0;
}


scanf_s() 함수로 값을 입력할 때 우리는 마지막에 입력이 끝났다는 의미로 엔터키(=개행문자)를 누른다.

그리고 scanf_s() 함수는 엔터키 즉, 개행문자를 꺼내지 않고 입력 버퍼에 그대로 남겨둔 상태로 종료된다.

그런데 이때 scanf_s() 함수로 문자열의 형태가 아닌 문자를 입력받는 함수를 사용하게 되면 문제가 발생한다.

바로 사용자에게 문자를 입력받지 않고 버퍼에 남아있던 개행문자를 인식하면서 문자 입력을 종료하게 된다는 것...!

 

문제코드1

#include <stdio.h>

int main(void)
{
	int num1 = 0;
	int num2 = 0;
	int num3 = 0;

	scanf_s("%d%d%d", &num1, &num2, &num3);
	printf("입력한 숫자는 %d, %d, %d입니다.\n", num1, num2, num3);

	char ch = 0;

	scanf_s("%c", &ch, sizeof(ch));
	printf("입력한 문자는 %c입니다.\n", ch);

	return 0;
}

문제코드2

#include <stdio.h>

int main(void)
{
	int num1 = 0;
	int num2 = 0;
	int num3 = 0;

	scanf_s("%d%d%d", &num1, &num2, &num3);
	printf("입력한 숫자는 %d, %d, %d입니다.\n", num1, num2, num3);

	char ch = getchar();
	printf("입력한 문자는 %c입니다.\n", ch);

	return 0;
}

 

이와 같은 문제를 해결하려면 버퍼에 남아있는 개행문자를 제거해야하는데, 형식 지정자 뒤에 %*c라는 형식 문자를 붙여주면된다.

 

문제해결1

#include <stdio.h>

int main(void)
{
	int num1 = 0;
	int num2 = 0;
	int num3 = 0;

	scanf_s("%d%d%d%*c", &num1, &num2, &num3);
	printf("입력한 숫자는 %d, %d, %d입니다.\n", num1, num2, num3);

	char ch = 0;

	scanf_s("%c", &ch, sizeof(ch));
	printf("입력한 문자는 %c입니다.\n", ch);

	return 0;
}

문제해결2

#include <stdio.h>

int main(void)
{
	int num1 = 0;
	int num2 = 0;
	int num3 = 0;

	scanf_s("%d%d%d%*c", &num1, &num2, &num3);
	printf("입력한 숫자는 %d, %d, %d입니다.\n", num1, num2, num3);

	char ch = getchar();
	printf("입력한 문자는 %c입니다.\n", ch);

	return 0;
}


형식 문자 기반 문자열 입력

문자열 입력 시 scanf_s() 함수를 사용하면 공백 문자를 사용할 수가 없다.(%[^\n]s라면 가능하긴 함..!)

그러기에 행 단위로 뭔가를 써야한다면 그냥 gets_s() 함수를 사용하는 것을 권장!


[필수 실습 문제] 나이와 이름 입/출력하기

#include <stdio.h>

int main(void)
{
	int age = 0;
	char name[10] = { 0 };

	/*
	%*c를 사용하지 않으면 gets_s() 함수에서 버퍼에 남아있는 개행문자를 읽고 입력이 끝났다고 생각해버리기 때문에 
	버퍼를 비워주는 역할을 하는 형식문자 %*c를 꼭 붙여주자
	*/
	printf("나이를 입력하세요: ");
	scanf_s("%d%*c", &age);

	printf("이름을 입력하세요: ");
	gets_s(name, sizeof(name));

	printf("당신의 나이는 %d살이고 이름은 '%s'입니다.\n", age, name);

	return 0;
}