C언어

10) 표준 입력 함수 - getchar, gets

SleeveStar 2021. 2. 25. 16:56
반응형

표준 입력 함수란?

 

 컴퓨터를 사용해서 프로그램을 개발하는 프로그래머에게 가장 기본적인 입력수단은 키보드이다.

하지만 꼭 입력을 키보드로 해야 하는 것은 아니다. 키보드 만큼이나 마우스도 사용하고 게임을 즐기는 사람들은

조이스틱을 사용하기도 한다. 이렇게 다양한 입력 장치중에 사용하는 시스템이 가장 기본으로 생각하는 장치를

'표준 입력 장치'라고 부른다.

 C언어는 이런 장치로부터 데이터를 입력 받는 표준 입력 함수를 제공한다.

 

표준 입력함수는 시스템의 여러가지 입력 장치를 표준화하기 위해, 장치의 입력 정보를 읽는 것을 컴퓨터가 파일에서 데이터를 읽는 것처럼 구성한다.

 

입력 값을 임시로 저장하는 표준 입력 버퍼

 

특정키를 누를 때까지 사용자 입력을 임시로 저장하는 메모리를 '표준 입력 버퍼'라고 한다. 이 입력 버퍼는 운영체제가 제공하는데, 표준 입출력을 사용하는 시스템을 위해 별도의 메모리가 배정되어 있다.

 

사용자 입력(키보드) > 표준입력 버퍼에 임시저장+Enter > 입력 받은 값을 프로그램에서 사용

 

그런데 여러 개의 정보를 입력하고 Enter키를 눌렀는데 입력 버퍼에 있는 내용을 전부 사용하지 않았다면 다음에 호출하는 표준 입력 함수에 영향을 미치므로 주의해야 한다.

 

예) 'abc'를 키보드에서 입력 >> 입력버퍼에 'abc'저장 >> Enter 입력 >> 문자를 한개만 입력 받는 표준 입력 함수이므로 입력 버퍼에서 a만 사용함 >>입력 버퍼에 있는 내용을 다 써버리지 못하고 b,c가 남음 >> 문자를 한개만 입력받는 표준 입력 함수를 다시 호출하면 남아있던 b부터 사용되어버림.

 

입력 버퍼를 초기화 하는 rewind 함수

 

입력 버퍼에 남아있는 입력 정보를 모두 지우고 싶다면 표준 입력 장치를 가리키는 stdin 포인터와 rewind 함수를 사용하여 표준 입력 버퍼를 초기화 하면 된다.

 

rewind(stdin); //rewind 함수를 사용하면 입력 버퍼를 초기화 할 수 있음

 

 

문자 한 개를 입력 받는 getchar 함수

 

표준 입력 함수중에서 자주 사용하는 함수 몇가지를 살펴보자. 표준 입력 함수는 stdio.h(standard Input Output Header)파일에 정의되어 있으므로 #include 전처리기를 사용해서 이 파일을 참조해야 한다.

 

getchar함수를 사용하여 키보드에서 문자 한 개 입력 받기

 

getchar함수는 int 형식으로 데이터를 반환한다. 그래서 int 형 변수로 반환값을 받는게 일반적이지만, char 형 변수로 받아도 된다.

 

*getchar 함수는 왜 char 형 변수로 반환값을 받아도 되나요?

 

stdio.h 헤더 파일을 열어 getchar 함수의 원형을 보면 int형을 반환하도록 되어 있다. 이것은 확장키 값(키브도의 F1이나 숫자 키패드, 또는 특수키)를 위해서 정의된 것이다.

하지만 우리가 일반적으로 사용하는 입력 문자들은 대부분 1바이트 정보를 가지고 있기 때문에 특수한 프로그램을 만들지 않는 이상 1바이트만 사용해도 충분하기 때문에 getchar 함수의 반환값을 char형 변수로 받아 사용해도 문제가 생기지 않는다.

 

getchar 함수를 사용할 때 주의할 점

 

 

첫번째 함수는 a를 입력 받아서 정상적으로 출력하지만 두 번째 getchar 함수는 첫 번째 입력 받을 때 함께 입력된 enter키의 값(10, \n)을 받게 되어 아무것도 출력하지 않고 줄 바꿈만 한번 더 일어난다는 것이다. 이 문제를 해결하는 방법은 여러가지다.

 

해결방법1

 

getchar 함수의 원래 기능인 '문자 한개 입력 받기'로 사용한 것이 아니라 단순히 enter 키 값이 출력되지 않도록 이 함수를 사용하는 것이다.

 

해결방법2

 

사용자가 한 번에 한개의 문자만을 입력한다고 확신할 수 없다면 위에서 설명한 방법으로는 문제를 해결할 수 없다. 즉

getchar 함수를 추가해서 함께 입력되는 Enter 키 값을 제거하는 방법은 사용자가 몇 개의 키를 입력할지 예상할 수 없는 상황에서는 근본적인 해결책이 아니다. 결국 이 문제는 사용자가 입력한 한개의 문자외에 다른 문자들이 입력 버퍼에 남아서 생기는 문제다.

 

위와 같이 입력된 키 중에 첫번째만 출력하고 나머지는 rewind 함수를 사용해서 입력 버퍼에 있는 나머지 입력 값을 지워버리는 것 또한 해결방법이다.

 

*getchat 함수는 또 다른 표준 입력 함수인 getc 함수로 대체할 수 있다.

 

 

문자열을 입력 받는 gets 함수

 

gets 함수는 get string의 줄임 표현이며 문자열을 입력 받는 표준 입력 함수이다.

 

getchar 함수와 달리 gets 함수는 한 번에 여러 개의 문자를 입력 받을 수 있으며 enter 키를 입력할 때까지 입력한 모든 문자를 하나의 문자열로 간주한다.

그리고 문자열을 저장하기 위해서 gets 함수의 매개변수에 char배열로 선언된 변수의 시작 주소를 넘겨주어야 한다.

 

char input_string[10];

gets(input_string);

 

*주소를 넘겨주는데 왜 &연산자를 쓰지 않는가?

= 배열의 각 요소는 메모리에 연속적으로 나열되기 때문에 배열의 첫 번째 요소의 시작 주소는 배열의 시작주소와 같다 그렇기 때문에 배열의 시작 주소를 적을 때 첫 번째 요소의 주소인 &input_string[0]으로 적는것이 정확한 표현이다. 하지만 첫 번째 요소의 시작주소와 배열의 시작주소는 같기 때문에, input_string이라고 적으면 컴파일러가 번열할 때 &input_string[0]이라 적은 것과 같게 번역한다.

 

gets 함수로 문자열 입력 받기

 

*gets 함수는 enter 키까지 입력 버퍼에서 읽어와 처리하므로 입력 버퍼에 enter키가 남아있지 않는다. 따라서 rewind 함수를 사용하여 입력 버퍼를 초기화할 필요가 없다. enter키를 입력한 위치에는 NULL 문자 0이 들어가게된다.

 

*gets 함수가 정이되어 있지 않다면 fgets 함수를 사용해라

 

char input_string[10];

fgets(input_string, 10, stdin);

 

표준 입력 장치를 의미하는 stdin을 사용하면 표준 입력 버퍼에서 문자열을 받아올 수 있다.

 

 

 

gets 함수에서 사용자 키 입력 취소 처리하기

 

 

표준 입력을 받는중에 프로그램의 중지를 의미하는 Ctrl + C키를 사용자가 입력하면 표준입력이 취소되고 프로그램이 중지된다.

 

키보드로 문자를 입력하던 중에 Ctrl + C키를 입력하면 입력 버퍼에 들어있는문자들이 input_string 배열에 복사되지 않은 상태로 gets함수가 종료된다. 그리고 printf에서 input_string 배열 내용을 그대로 출력하므로 엉뚱한 값이 출련된다.

 

따라서 gets함수를 사용하여 문자열을 입력 받을 때는 사용자가 정상적으로 입력을 완료하지 않는 상황에 대처할 수 있도록 코드를 구성해야 한다.

 

이것은 gets 함수의 반환값을 확인해서 처리가 가능하다.

gets 함수는 사용자 입력이 정상적으로 완료되지 않았다면 '해당하는 메모리 주소가 없음'을 의미하는 NULL 값을 반환하고, 성공적으로 입력했다면 input_data 배열의 시작 주소를 반환한다.

 

gets 함수로 문자열을 입력 받을 때 입력 취소까지 처리하기

 

 

 

gets 함수에서 키 입력 횟수 제한하기

 

gets 함수는 입력 받은 문자열의 개수를 제한하는 기능이 없기 때문에 자신이 예상한 길이보다 더 긴 문자열이 입력되는 경우에 프로그램이 비정상적으로 종료된다.

그렇기 때문에 프로그래머가 직접 비슷한 함수를 프로그래머가 직접 만들어서 이 문제를 해결해야 한다.

 

 

문자열을 정수로 변환해 사용하기

 

gets함수를 사용하다 보면 오류는 아니지만 또 다른 불편함이 있다. 

 

gets 함수가 문자열을 입력받는 함수이기 때문에 "12345"와 같이 숫자 형태로 입력하더라도 정수가 아닌 문자열로 인식한다. 이렇게 문자열로 인식하게되면 산술 연산이 불가능해진다.

 

int data = "12345"; // 오류: 문자열은 정수형 변수에 대입 할 수 없다.

"12345" + "100" // 오류: 문자열 간의 덧셈 연산은 불가능하다.

 

1단계 : 아스키코드 표를 이용해 문자열을 정수로 변환하기

 

문자열을 정수값으로 변환하는 법은 간단하다. 컴퓨터에서 사용하는 문자들을 숫자와 연결시켜 주는 아스키 코드 표에서 숫자는 '0'부터 '9'까지 순차적으로 나열되어 있다. 각 문자로 된 숫자의 아스키 코드 값을 확인해 보면 문자 '0'이 48,

문자 '1'이 49, 문자'2'가 50, 문자 '3'이 51 ... 의 순서로 되어 있다.

따라서 문자로 된 숫자의 시작 값인 문자 '0'의 아스키코드 값을 각 숫자 형식의 문자에서 빼면 정수형태의 숫자 값으로 바뀌게 된다.

 

'0' > '0' -> 48 - 48 ->  문자'0'이 숫자 0으로 변환

'1' > '0' -> 49 - 48 ->  문자'1'이 숫자 1로 변환

'2' > '0' -> 50 - 48 ->  문자'2'이 숫자 2로 변환

 

"123"이라는 문자열을 숫자 123으로 변환하고 싶다면 각 숫자를 100의자리, 10의자리, 1의자리에 맞춰서 123으로 재구성해야 한다.

 

'1'-'0', '2'-'0', '3'-'0' >> 1,2,3

 

그 다음으로 1은 100의 자릿수이기 때문에 100을 곱하고 2는 10의 자릿수이기 때문에 10을 곱하고 3은 1의 자릿수이기 때문에 1을 곱해서 이 수들을 합산하면 123이 된다.

 

('1'-'0') x 100 + ('2'-'0') x 10 + ('3'-'0') x 1 = 1 x 100 + 2 x 10 + 3 x 1 

 

위와 같은 기능을 소스 코드로 구성해보자

 

문자열 "123"을 정수 123으로 변경하려면 반복문을 사용하여 배열의 각 요소에 들어 있는 문자를 숫자로 만들어 주어야 한다.

 

문자를 숫자로 만드려면 num_string[i]에 저장된 문자값에서 아스키코드 '0'을 빼면 된다. 그리고 문자를 숫자로 변경하면서 현재 자릿수에 해당하는 값을 가지고 있는 pos_num을 곱해서 num이라는 변수에 합산하는 형식으로 작업을 진행하면 최종적으로 num에 정수 값 123이 저장된다.

 

길이 제한 없이 문자열 형식의 숫자를 정수로 변환하기(1)

 

숫자 형식의 문자열을 정수로 만드는 좀 더 간단한 표현

 

지금까지 본 예제는 자릿수에 곱할 숫자를 미리 구성하고 변환하는 방식을 사용했기 때문에 코드가 복잡하다.

자릿수에 곱할 숫자를 미리 구성하지 않고 문자열 순서대로 각 문자를 정수로 만들면서 명령을 반복할 때마다 10을 곱하면 결국 같은 결과 값을 얻을 수 있다. 수학 식으로 표현해 보면 두 수식의 결과 값은 같다.

 

1 x 100 + 2 x 10 + 3 x 1

1 x 100  > 100 + 2 x 10 > 120 + 3

 

 

((1 x 10) + 2) x 10 + 3

1 > 1 x 10 + 2 > 12 x 10 + 3

 

 

새롭게 사용할 방식은 배열의 각 요소값을 계산할 때 별도의 자릿수 값을 곱하는 것이 아니라 일정하게 10을 곱해서 사용하기 때문에 문자열의 길이도 계산할 필요가 없다.

 

 

길이 제한 없이 문자열 형식의 숫자를 정수로 변환하기(2)

 

 

gets 함수를 사용하여 두 개의 숫자를 입력 받아 합산하기

 

위 예제는 사용자가 숫자를 입력할 때 너무 길게 입력하면 오류가 발생할 수 있기 때문에 gets 함수 대신 앞에서 만든 GetMyString 함수를 사용해서 입력개수를 제한하는것이 더 좋다.

 

fgets 함수를 사용하면 문자열 끝에 \n이 추가되기 때문에 정상적인 결과가 나오지 않는다 왜냐하면 5행에서 string[count] 값이 문자열 끝을 의미하는 NULL 문자 0과 같을 때까지 반복하기 때문에 \n도 숫자로 처리되어 버린다.

fgets 함수를 사용하려면 5행을 다음과 같이 수정해야한다.

 

while(string[count] != 0 && string[count] != '\n') {

이렇게 하면 string[count] 값이 0이나 '\n'과 같을때 중단되기 때문에 \n 문자가 숫자로 변환되어 문제가 해결된다

 

atoi 함수를 사용하여 문자열을 정수로 변환하기

 

ArrayToInteger 함수는 C언어 표준 라이브러리에서 제공하는 atoi 함수와 같은 기능을 수행한다. ArrayToInteger 함수를 굳이 직접 만들지 않고 atoi 함수를 사용해서 작업해도 되는 것이다. 

 

atoi 함수는 stdlib.h 파일에 함수의 원형이 정의되어 있으므로 이 함수를 사용하려면 stdib.h 파일을 해당 소스 파일에 include해야 한다.

 

 

 

반응형

'C언어' 카테고리의 다른 글

11) 배열과 포인터 표기법  (0) 2021.03.05
10-2) 표준입력함수 - scanf  (0) 2021.03.03
9-1) 포인터 변수의 주소 연산  (0) 2021.02.08
9)포인터  (0) 2021.01.30
8) 배열  (0) 2021.01.24