Chapter 4 문자 입력 받기

https://raw.githubusercontent.com/KuraiLuna/KuraiLuna.github.io/master/practical/C/%EC%94%B9%EC%96%B4%EB%A8%B9%EB%8A%94%20C%20%EC%96%B8%EC%96%B4/Chapter-04/Chapter-04-%EB%AC%B8%EC%9E%90-%EC%9E%85%EB%A0%A5-%EB%B0%9B%EA%B8%B0.gif

문자 입력 받기

지난번 기록은 잘 이해 되었으면한다

이번 기록에서는 제목에서도 볼 수 있듯이

두 가지 내용을 한꺼번에 배우게 된다.

바로, 문자를 키보드로

부터 입력을 받는 것이지.

문자를 입력 받을 수 있다면,

숫자도 당연히 입력 받을 수 있게 된다.

즉, 이번 강좌에서는 문자 형식의 변수와

키보드로 부터 입력을 받는

입력에 대해 알아 보도록 하겠다.

일단, 컴퓨터에서

문자를 처리하는 방식에

대해 생각해 보도록하자.

우리의 컴퓨터는 그다지 똑똑하지 못하다.

아무리 최신 Intel CPU 를 장착해도

컴퓨터는 단지 0 과 1 만을 처리할 뿐.

따라서, 2 와 3 같은 숫자도 처리하지 못하는데

어떻게 a, b 가, 나, 韓 과

같은 수 많은 문자를 처리할 수 있겠는가?

하지만, 방법이 있다.

이러한 문자들을 숫자에 대응시키는 것입니다.

그런데, 숫자에 대응시킨다면

컴퓨터가 이 것이 숫자인지,

아니면 문자인지 어떻게 알까?

물론 알 방법은 없다.

단지 이 숫자를 ‘문자’ 형태로

사용하거나 ‘숫자’ 형태로 사용하는 것.

문자를 저장하는 변수는

앞에서 살짝 본 적이 있다.

바로 char 이다.

int integer

약자였다면 char

character 의 약자 다.

변수가 등장하면 어김없이 등장하는

아래의 표를 살펴 보도록 하자.

https://raw.githubusercontent.com/KuraiLuna/KuraiLuna.github.io/master/practical/C/%EC%94%B9%EC%96%B4%EB%A8%B9%EB%8A%94%20C%20%EC%96%B8%EC%96%B4/Chapter-04/1126BC1149F5682ED85700.webp
 

보시는 것과 같이 char 은 맨 위에

위치해 있으며 크기는 1 바이트.

또한, 이를 통해 나타낼 수 있는

숫자의 범위를 알려주고 있는데,

이는 -128 부터 127 까지, 256 까지 다.

/* 문자를 저장하는 변수 */
#include <stdio.h>
int main(){
    char a;
    a = 'a';

    printf("a 의 값과 들어 있는 문자는? 값 : %d , 문자 : %c \n", a,a);
    return 0;
}

위 소스를 성공적으로 컴파일 했다면

char a;

이 부분은 char 형 변수를 선언하는 부분.

기억이 안나시는 분들은 3장으로 되돌아 가보자

 
a = 'a';

C 컴파일러는 이 a 가 변수 a 라고

착각하여 a 라는 변수의 값을 a 라는

변수에 대입하는 문장으로 인식하게 된다.

따라서 a 에는 아무런 값이

들어있지 않은 쓰레기 값(NULL) 이 되어

나중에 a 라는 문자를 출력해 보았을 때,

이상한 값이 나오게 된다.

문자를 대입하는 것도

숫자를 대입하는 것과 동일.

대입 연산자를 이용하면 된다.

printf("a 의 값과 들어 있는 문자는? 값 : %d , 문자 : %c \n", a, a);

마지막으로,

위 printf 문에 대해 보도록 하겠다.

앞에서 말했듯이 컴퓨터는 a 가

문자라는 것 자체를 모른다고 했다.

단지 우리가 a 를 문자로 보느냐

아니면 숫자를 보느냐에

따라 달라진다고 했는데,

이 말 뜻을 위 printf 문을 보면 알 수 있다.

일단, %d 는 a 의 값을

숫자 (정수인 10 진수) 라고

출력하라는 뜻.

그 옆의 %c 는 아마 예상했겠지만

a 의 값을 문자로 출력하라는 뜻.

따라서, %c 에는 a 에

저장되어 있던 문자 ‘a’ 가 출력되게 된다.

그렇다면 %d 에는 무엇이 출력되었을까?

앞에서 말했지만 컴퓨터는 문자와 숫자를 일대일

대응 시켜서 생각한다고 했다. 따라서,

%d 에 출력되는 숫자가

바로 a 에 대응되는 숫자를 가리킨다.

이 때, 각 문자 마다 대응되는 숫자를

아무렇게나 하는 것이 아니라

일정하게 정해져 있는데

현재 우리가 쓰고 있는

컴퓨터에서는

다음과 같이 정의되어 있다.

https://raw.githubusercontent.com/KuraiLuna/KuraiLuna.github.io/master/practical/C/%EC%94%B9%EC%96%B4%EB%A8%B9%EB%8A%94%20C%20%EC%96%B8%EC%96%B4/Chapter-04/16506D0949F56DF8DED1B5.webp

위 표는 미국 표준 학회(ASA) 에서

정한 아스키

(ASCII, American Standard

Code for Information Interchange)

코드로8 비트 데이타를 이용하여

여러 문자에 번호를 붙인 것.

아까, a 의 숫자 값을 출력하였을 때

97 이 나왔는데 위 표에서 찾아 보면

a 의 값이 97 임을 볼 수 있다.

이 때, 위 표의 내용이 0 부터 127 까지

밖에 없는 이유는 위 표준을 정할 당시

그 당시 7 비트 만으로 충분하다고

생각했기 때문.

하지만 IBM 에서 좀 더 많은

종류의 문자가 필요하게 되자

1 비트를 더 추가 시켜서

확장된 아스키 코드

(Extended ASCII Code) 를

만들었다.

하지만 위 256 개

가지고는 충분하지 못하다.

왜냐하면 우리 글만 해도

자모음 24 개로 구성되어 있는데,

한 글자당 최대 초성/중성/종성 을

모두 표현해야 합다.

또한 더욱 심각한 것은

한자와 같은 표의문자의

경우 수만 개가 넘는

한자 데이터들을 가지고

있어야 하는데 이를 256 개 안에 다

표현한다는 것은 불가능하기 때문.

따라서, 컴퓨터가 전세계에 보급되자

좀 더 많은 종류의 문자를

표현해야 한다는 필요성이 대두되었다.

결국에는 유니 코드(Unicode) 라는

새로운 형식의 문자 체계를 도입하게 된다.

유니코드는 한 문자를 1 에서 4 바이트까지

다양한 길이로 처리합니다. 이는,

기존 아스키 코드의 체계를 유지하면서,

새로운 문자들을 추가하기 위함이다.

여러분은 아직 유니코드를

직접 다룰 일은 없기 때문에

여기서는 무시하셔도 괜찮다.

scanf 의 도입

/* 섭씨온도를 화시로 바꾸기 */
#include <stdio.h>
int main() {
    double celsius;  // 섭씨 온도

    printf("섭씨 온도를 화씨 온도로 바꿔주는 프로그램 입니다. \n");
    printf("섭씨 온도를 입력해 주세요 : ");
    scanf("%lf", &celsius);  // 섭씨 온도를 입력 받는다.

    printf("섭씨 %f 도는 화씨로 %f 도 입니다 \n", celsius, 9 * celsius / 5 + 32);

    return 0;
}

위 소스를 성공적으로 컴파일 했다면 아래와 같이 나온다.

https://raw.githubusercontent.com/KuraiLuna/KuraiLuna.github.io/master/practical/C/%EC%94%B9%EC%96%B4%EB%A8%B9%EB%8A%94%20C%20%EC%96%B8%EC%96%B4/Chapter-04/scanf_%EC%9D%98_%EB%8F%84%EC%9E%85.png
 
이 때, 원하는 숫자를 쓴 후 엔터를 누른다면 (예 : 255)
 
https://raw.githubusercontent.com/KuraiLuna/KuraiLuna.github.io/master/practical/C/%EC%94%B9%EC%96%B4%EB%A8%B9%EB%8A%94%20C%20%EC%96%B8%EC%96%B4/Chapter-04/scanf_%EC%9D%98_%EB%8F%84%EC%9E%852.png
 
 
 

위와 같이 섭씨가 화씨 온도로

변경된 값이 출력된다.

와우! 드디어 괜찮은

프로그램을 처음으로 만들어 보게 된 것.

소스 코드를 찬찬히 살펴 보도록

double celsius;  // 섭씨 온도

일단, celsius 라는 double 형 변수를 선언하였다.

변수의 이름을 종전의 a , b 에서 celsius 라고

한 이유는 좀 더 이해하기 편하기 때문.

좋은 소스 코드의 조건은 다른 사람이

이해하기 쉬운 소스 코드 이고,

다른 사람이 이해하기 쉬운 소스코드는

기본적으로 변수 이름을 보고도

변수를 한 눈에 파악하기 쉽게 만드는 것.

scanf("%lf", &celsius);  // 섭씨 온도를 입력 받는다.

이제, 새로운 것이 등장하였다.

printf 에 이어 등장한 scanf 군.

printf 가 화면에 결과를 출력해 주는

함수였다면, scanf 는 화면(키보드) 로

부터 결과를 받아들이는 입력 함수다.

이렇게 흔히 printf 와

scanf 를 가리켜

입출력함수라 한다. 이 때,

scanf 함수는 우리가

어떠한 입력을 하기

전까지 계속 기다립니다.

또한, 입력을 할 때 엔터를

눌러야지만 입력으로 처리된다.

scanf 와 printf 는

이름도 비수무리 할 뿐더러,

사용하는 방법도 비슷합니다.

printf 에서

각 변수를 출력할

포맷(%d, %f, %c 등) 을 변수마다

다르게 하는 것처럼 scanf 도 각

변수의 타입마다 입력받는

포맷을 달리 해야 한다.

위 경우 처럼

double 형의 변수를

입력 받으려면

%lf (소문자 LF 이다, if 가 아니다)

로 해야 한다.

그런데, printf 보다 조금 까다로운 점은

printf 는 double 이나 float

모두 %f 로 출력하지만

이에 경우 float 은 %f 로 무조건

입력 받아야 한다는 점.

마찬가지로 double 형 변수도

무조건 %lf 로만 입력 받아야 한다.

그 외에도, printf 는 정수형 변수는

모두 %d 로 출력 가능했던

반면에 scanf 는 각 자료형

마다 포맷이 다 정해져 있다.

아래 예제에서 잠시 scanf 의

포맷 들에 대해 정리해 보도록 하겠다

printf("섭씨 %f 도는 화씨로 %f 도 입니다 \n", celsius, 9 * celsius / 5 + 32);

마지막으로 위 프로그램의

중요한 부분을 살펴보자.

바로 이 부분에서 섭씨와 화씨의

환산 작업이 이루어 진다.

참고로, 화씨와 섭씨의

변환 공식은 아래와 같다.

https://raw.githubusercontent.com/KuraiLuna/KuraiLuna.github.io/master/practical/C/%EC%94%B9%EC%96%B4%EB%A8%B9%EB%8A%94%20C%20%EC%96%B8%EC%96%B4/Chapter-04/scanf_%EC%9D%98_%EB%8F%84%EC%9E%853.png

따라서, 이 공식을 그대로

C 언어 수식을 바꾼 것이

9 * celsius / 5 + 32 인 것.

곱셈과 나눗셈의 우선순위가

높으므로 9 * celsius / 5 가

먼저 계산 된 후 32 가 더해지므로

위의 식과 일치한다.

따라서 printf 의 두번째

%f 부분에는 위 계산된

화씨의 값이 들어가게 된다.

/* scanf 총 정리  */
#include <stdio.h>
int main() {
    char ch;  // 문자

    short sh;  // 정수
    int i;
    long lo;

    float fl;  // 실수
    double du;

    printf("char 형 변수 입력 : ");
    scanf("%c", &ch);

    printf("short 형 변수 입력 : ");
    scanf("%hd", &sh);
    printf("int 형 변수 입력 : ");
    scanf("%d", &i);
    printf("long 형 변수 입력 : ");
    scanf("%ld", &lo);

    printf("float 형 변수 입력 : ");
    scanf("%f", &fl);
    printf("double 형 변수 입력 : ");
    scanf("%lf", &du);

    printf("char : %c , short : %d , int : %d ", ch, sh, i);
    printf("long : %ld , float : %f, double : %f \n", lo, fl, du);
    return 0;
}

성공적으로 컴파일 후

(경고가 6 개 정도 나올 수 있는데)

(무시하자(´・ω・`) )

d:\□□□□□_fFF□\□□□g□□□m□□□\F\>test
char 형 변수 입력 : b
short 형 변수 입력 : 1
int 형 변수 입력 : 2
long 형 변수 입력 : 3
float 형 변수 입력 : 4
double 형 변수 입력 : 5
char : b , short : 1 , int : 2 long : 3 , float : 4.000000, double : 5.000000
d:\□□□□□_fFF□\□□□g□□□m□□□\F\>
printf("char 형 변수 입력 : ");
scanf("%c", &ch);

일단, 제일 먼저 문자를 입력 받는 부분을 보도록하자.

예전에도 이야기 했지만 한글은 2 바이트

이상을 차지하기 때문에 최대 1 바이트를

차지하는 char 형 변수인 ch 에

한글을 치면 오류가 생긴다.

이와 같이 허용된 메모리 이상에

데이터를 집어넣어 발생하는

오류를 버퍼 오버플로우(Buffer Overflow)

라고 하며 보안 상 매우 취약하다.

 

 버퍼 오버플로우를 이용해서 공격자들이 프로그램의 정상적인 코드가 아니라,

 자신들이 원하는 코드가 실행될 수 있도록 조종할 수 있습니다.

a

 

 뿐만 아니라 근처의 데이터가 손상됨에 따라 큰 문제가 

 발생하게 될 수 도 있다. 따라서, 여러분들은

a

 

 

 버퍼 오버플로우가 일어나지 않게 허용된 데이터 이상을 집어넣는지

 안집어 넣는지 검사해야 한다.

a

또한 앞으로 우리가 char 형 변수를

선언할 때 에는 이 사람이 문자를

보관하는 변수를 선언하는 구나 라고

생각하도록 하자. 왜냐하면 보통

정수 데이터를 보관하는 변수로는 int 를 쓰지

char 을 잘 쓰지 않을 뿐더러 char 이름도

character 에서 따왔을 만큼

문자와 무언가 관련이 있기 때문.

printf("short 형 변수 입력 : ");
scanf("%hd", &sh);
printf("int 형 변수 입력 : ");
scanf("%d", &i);
printf("long 형 변수 입력 : ");
scanf("%ld", &lo);

이 부분은 여러분들이 무난하게

이해하실 수 있으리라 본다.

단지 포맷에 %hd, %d, %ld 로 다른 것 뿐.

참고로 short 형이나 long 형은 아직

다루지는 않았지만 int 와

똑같은 계열의 정수형

변수라고 생각하시면 된다.

printf("float 형 변수 입력 : ");
scanf("%f", &fl);
printf("double 형 변수 입력 : ");
scanf("%lf", &du);

마찬가지로 float 형에서는 %f 로,

double 형에서는 %lf 로 사용한다는

것을 기억하도록 하자.

 정 리

  • char 은 1 바이트 정수를 저장하는 타입으로,

     주로 문자를 저장하는데 사용됩니다.

   • 각 문자들은 아스키 테이블이란 표를 통해 특정 정수와

      대응되어 있습니다. 예를 들어서 65 는 알파벳 A 와 대응됩니다.

   • scanf 를 통해 사용자로 부터 데이터를 받을 수 있습니다.

   • %c 는 문자, %d 는 정수, %f 는 float, %lf 는 double 을 받습니다.

 

https://raw.githubusercontent.com/KuraiLuna/KuraiLuna.github.io/master/practical/C/%EC%94%B9%EC%96%B4%EB%A8%B9%EB%8A%94%20C%20%EC%96%B8%EC%96%B4/Chapter-04/%EC%98%A4%EB%8A%98%EB%8F%84_%EC%A2%8B%EC%9D%80_%ED%95%98%EB%A3%A8.png