1. 문자의 기본 입출력
1-1. 아스키코드
- 컴퓨터는 문자를 저장할 때 문자 그 자체의 모양을 저장하지 않는다.
- 사실 문자라는 건 없다고 생각하는 것이 좋다. 문자는 약속된 정수다.
- 각 문자마다 고유의 번호를 붙이고 표로 만들어 둔 뒤, 해당 번호를 출력할 때, 자료형이 '문자'일 경우 미리 저장되어 있는 표를 보고 해당 문자를 그려준다.
- 그런데 개발자마다 다른 표를 사용한다면 서로의 코드가 호환되지 않을 것이다. 그래서 표준화된 약속이 필요한데, 이를 아스키코드라고 부른다.
- 초기에는 1byte중 7bit에만 문자정보를 담았다가 나중에 확장 아스키코드라고도 불리는 ansi 코드로 발전했다.
- ansi 코드는 1byte를 전부 사용한다. (8bit == 256개)
- 그러다가 코딩이 세계 각국으로 퍼지면서 표에 담아야할 문자들이 점점 늘어나서 유니코드라는 표준을 사용하기 까지에 이르렀다고 한다.
- 즉 C언어에서 문자는 결국 "정수"이기 때문에 변환도 가능하며 증감 및 비교 연산또한 가능하다.
#include <stdio.h>
int main(void)
{
char small, cap = 'G';
if (cap >= 'A' && cap <= 'Z') //아스키 코드상에서 A부터 Z까지 연속된 숫자로 되어 있기 때문에
small = cap + ('a' - 'A');//비교연산을 이용해서 해당 문자가 대문자인지 확인할 수 있다.
//뿐만 아니라 대/소문자의 차이를 더해서 대문자를 소문자로 바꿀 수 있다.
//아스키 코드 상에서는 소문자가 더 값이 크기 때문이다.
printf("대문자 : %c %c", cap, '\n');
printf("소문자 : %c", small);
return (0);
}
1-2. scanf로 문자 입력
- 전 챕터에서 배웠듯, scanf는 키보드에서 입력을 받는 함수라고만 하면 반만 맞는 설명이고, 입력을 저장한 버퍼에서 값을 읽어오는 함수라고 해야 정확한 설명이다.
- 위와 관련해서 scanf에서 문자를 입력받는 경우에는 white space를 주의해야한다.
- white space란 개행이나 공백, 캐리지 리턴과 같은 데이터를 구분하는 용도로 쓰이는 특수한 문자를 말한다.
- 문자가 아닌 다른 자료형의 경우 scanf가 white space를 무시하지만 문자의 경우에는 white space 또한 문자로서 읽어버리기 때문에 개발자의 의도와 다른 동작을 할 수 있다.
- 아래 코드는 scanf로 문자를 2개 입력 받을 목적으로 만든 코드다.
- 다른 자료형의 경우 두개를 입력 받을 경우 공백을 구분해서 입력해야 했다.
- (ex) 정수 2개를 입력 받을 경우 "4 6"
- 컴파일러가 이렇게 구분하지 않으면 "4 6"과 "46"을 구분할 수 없기 때문이다.
- 하지만 문자는 한글자씩 읽어오기 때문에 구분문자로 white space를 쓸 수 없다. (사실 있다)
- 만약 아래와 같은 코드에서 "ab"를 입력하면 ch1과 ch2에 각각 a와 b가 저장되지만 "a b"를 입력하면 ch1와 ch2에 a와 " "가 각각 저장된다.
#include <stdio.h>
int main(void)
{
char ch1, ch2;
scanf("%c%c", &ch1, &ch2);
printf("[%c%c]", ch1, ch2);
return (0);
}
더보기
//case 1
입력 : ab
출력 : [ab]
//case 2
입력 : a b
출력 : [a ]
- 위와 같은 경우가 싫다면 scanf의 %와 c사이에 공백을 넣어주면 된다.
#include <stdio.h>
int main(void)
{
char ch1, ch2;
scanf("%c%c", &ch1, &ch2);
printf("[% c% c]", ch1, ch2); //이제 공백은 입력의 구분문자로 쓰인다.
return (0);
}
1-3. putchar와 getchar로 문자 입출력
- 전에 gets와 puts함수에 대해서 소개한 적이 있다.
- 위 함수의 문자 전용 버전이 있는데 이것이 getchar와 putchar 함수다.
- 프로토 타입(함수의 원형)아래와 같다.
int getchar(void);
int putchar(int);
- 프로토 타입(함수의 원형)아래와 같다.
- getchar 함수는 호출하면 scanf와 동일하게 사용자로부터 입력받아 버퍼에 저장하고 버퍼에서 한글자만큼 읽어온다.
- 즉, scanf에서 주의점이 동일하게 존재한다. 문자를 2개 받고 싶으면 버퍼를 비워줘야 한다.
- 반환 값은 해당 문자에 해당하는 아스키코드 값이다. (C에서 문자는 아스키 코드 기준 1byte만 사용하는 정수다.)
- 왜 char 형태가 아니라 int의 형태로 반환하냐면 문자와 문자가 아닌 것을 정확하게 구분하기 위해서다.
- 만약 입력을 그만 받고 싶다면 cmd + d (mac, linux) 이나 ctrl + z (window)를 눌러서 EOF (end of file)을 입력하여 입력을 종료할 수 있다.
- 이 EOF는 stdio.h 에 (-1)로 매크로 상수로 정의되어 있다.
- 그런데 반환을 char로 해버리면 1byte에 1로 가득찬 255번 문자와 4byte에 1로 가득찬 -1를 구분할 수 없다.
- 그래서 getchar는 EOF와 문자를 구분해야 하기 때문에 int 값을 반환하고, 입력 받은 값이 문자임이 확인되 면 4byte 중 가장 마지막 1byte로 문자를 구분하고 해석한다.
111111111 11111111 11111111 11111111 (EOF -1에 해당)
000000000 00000000 00000000 11111111 (255번 문자에 해당)
만약 char가 반환 자료형이라면 가장 마지막 1byte의 bit로만 값을 구분하기 때문에
EOF와 255번 문자를 구분할 수 없다.
- putchar 함수는 매개변수로 들어온 값의 문자를 출력하고 해당 문자의 숫자를 다시 반환한다.
- 만약 출력과정에서 오류가 발생하면 -1을 반환한다.
- 이 동작 또한 putchar 가 char가 아니라 int를 반환하는 주된 이유이다.
- 중요해서 다시 한번 상기시키면 C에서 문자는 "마지막 1byte만 사용하는 정수"다.
#include <stdio.h>
int main(void)
{
int ch;
ch = getchar(); //한 글자 입력하면
printf("input char : ");
putchar(ch); // 그 문자가 출력된다.
putchar('\n');
return (0);
}
2. 버퍼
2-1. 버퍼 개요
- 버퍼는 운영체제 단에서 할당하는 저장공간이다.
- 만약 컴퓨터가 키보드의 기종이 바뀌면 값이 입력되지 않다면 얼마나 불편할까
- 혹은 우리가 입력한 내용들을 한글자씩 그때 그때 컴퓨터가 처리한다면 얼마나 비효율 적일까
- 예를 들어서 마트에서 장을 보는데 바구니에 한번에 담았다가 계산하러 가는 것이 아니라 하나씩 매번 왔다갔다 하면서 계산한다고 생각해보라..
- 위의 내용들을 해결하는 방법으로 "버퍼"라는 공간을 사용하도록 설계되어 있다.
- 우리가 값들을 입력하면 이 것들은 곧바로 컴퓨터가 처리하는 것이 아니다.
- 값을 입력받으면 정해진 신호가 오기 전까지 이 "버퍼"라는 공간에 차곡차곡 담았다가, 신호가 들어오면 이 내용들을 한번에 컴퓨터에게 전달할 수 있다.
- 또한 버퍼라는 공간을 사용하기 때문에 키보드나 외부 입력장치가 변한다 하더라도 전부 호환되게 사용할 수 있다.
- 위에서 말한 신호는 \n (개행)으로 우리가 값을 입력하고 엔터를 눌러서 개행을 입력하면 개행까지 포함된 내용이 그 때 컴퓨터에게 전달되게 된다.
- 또한 위에서 언급한 EOF 또한 버퍼에 보내도록 하는 신호(시그널은 아니다! 주의!)로 입력을 종료할 때 사용된다.
- 만약에 우리가 무한으로 도는 while문 안에서 입력을 받을 때, 종료하려면 EOF가 필요하다.
- 개행은 버퍼의 내용을 컴퓨터로 전달하는 문자이기 때문이다.
2-2. scanf활용
- 위 개요에서 말한 내용을 scanf를 통해서 보면
- scanf는 버퍼에 내용이 담겨져 있다면 우리가 요청한 자료형의 크기만큼 읽어오고 아니라면 버퍼의 입력을 사용자로 부터 받게 해주는 함수다.
- 아래코드에서 만약에 hello world!를 입력하면 버퍼에 h e l l o " " w o r l d ! 가 담긴다. (" "가 공백 한칸을 의미한다.)
- 그럼 while(1)로 계속 돌면서 버퍼에 담겨져 있는 마지막 데이터 !까지 scanf가 읽으면서 출력한다.
- 그럼 한글자씩 출력되어 우리 눈에는 우리가 입력한 hello world! 가 출력되는 것을 볼 수 있다. 그리고 !까지 출력했다면 버퍼는 지금 비어있는 상태이므로 다시 사용자에게 버퍼를 채우라고 입력을 요구한다.
- 여기서 while 문을 빠져나와 프로그램을 종료하려면 scanf의 반환값이 -1이어야 하는데 이는 EOF를 입력해줌으로써 만족할 수 있다.
- scanf는 본인이 읽어온 자료형의 수만큼을 반환하는 데, EOF는 특수한 경우여서 이 경우에 -1를 반환한다.
#include <stdio.h>
int main(void)
{
int res;
char ch;
while(1)
{
res = scanf("%c", &ch);
if (res == -1)
break;
printf("%d ", ch);
}
return (0);
}
더보기
//입력값 : hello world!
출력값 : hello world!
2-3. getchar활용
- getchar 또한 마찬가지다. 다만 다른 점이라면 scanf는 입력받을 변수를 매개변수로 받지만 getchar는 그런 것이 없으므로 따로 배열을 선언해주었다.
#include <stdio.h>
void my_gets(char *str, int size)
{
char ch;
int i;
for (i = 0; i < size; i++)
{
ch = getchar();
if (ch != '\n')
str[i] = ch;
else
break;
}
str[i] = 0;
}
int main(void)
{
char str[7];
my_gets(str, sizeof(str));
printf("input line : %s\n", str);
return (0);
}
3. 혼공C 11장 도전 실전 예제
- 키보드로 입력한 단어 중 길이가 가장 긴 단어의 길이를 출력하는 코드
- eof가 들어올 때 까지 입력한 단어 중에서 길이가 가장 긴 단어를 출력
#include <stdio.h>
int main(void)
{
int cnt = 0, tmp = 0, ch;
ch = getchar();
while(ch != -1)
{
if (ch == '\n')
{
if (tmp > cnt)
cnt = tmp;
tmp = 0;
}
else
tmp++;
ch = getchar();
}
printf("가장 긴 단어의 길이 : %d\n", cnt);
return (0);
}
'Language > C' 카테고리의 다른 글
C 저수준 파일 입출력 - open(), close(), write(), read() (0) | 2024.11.20 |
---|---|
혼공C (10장) - 배열과 포인터 : 배열을 인자로 (0) | 2024.04.02 |
혼공C (9장) - 포인터, 상수 포인터와 포인터 상수 (0) | 2024.03.27 |
혼공C (8장) - C 배열, VLA, 문자열 혼공C 8장 실전예제 (1) | 2024.03.24 |
혼공C (7장) - C 함수 기초, 혼공C 7장 실전예제 (0) | 2024.03.22 |