C언어) 입출력 이야기 1

Lapis0875·2022년 12월 12일
0

c언어

목록 보기
17/21
post-thumbnail

🖥️ 입출력 이야기 (1)

우리는 프로그램을 사용하면서, 여러 수단을 통해 프로그램에 값을 입력해주고, 프로그램에 계산한 결과를 출력을 통해 전달받아요. 오늘은 콘솔을 활용한 입출력 방법들에 대해 알아볼게요.

1. 한 문자씩 입출력하기

간단하게, 한 문자씩, char 형의 값으로 입력받고 출력하는 함수들이 있어요.

getchar

int getchar(void);

형태로 정의된 이 함수는, 표준 입력 장치로부터 한개의 문자를 읽어들여요. 만약 읽을 문자가 없을 경우는 EOF를 반환해요.

putchar

int putchar(int c);

형태로 정의된 이 함수는, 인자로 전달한 한개의 문자를 표준 출력 장치에 써요.

표준 입출력 장치

표준 입력 장치는 기본 값으로는 키보드에서 직접 입력해주는 입력 스트림을 가리키고, 표준 출력 장치는 기본적으로 프로그램의 콘솔이에요. 프로그램이 사용하는 표준 입출력 장치는 변경할 수 있어요.

입출력 재지정

$ program < input > output

> 연산자를 사용해, program의 표준 입력 장치를 input 파일로 설정해요.
< 연산자를 사용해, program의 표준 출력 장치를 output 파일로 설정해요.

파이프

$ program1 | program2 | program3

program1의 출력 결과를 program2의 입력으로 전달하고,
program2의 출력 결과를 program3의 입력으로 전달해요.

program2의 입장에서 보면, program1이 표준 입력 장치이고 program3가 표준 출력 장치가 돼요.

한 문자씩 읽고 쓰기

간단하게, 콘솔에서 한 문자씩 읽어 바로 출력하는 프로그램을 작성해볼게요.

// singlechar.c
#include <stdio.h>

int main(void)
{
	char c;
    while ( (c = getchar()) != EOF)
    	putchar(c);
    return 0;
}

위 프로그램을 실행해보면, 아래와 같은 결과를 확인할 수 있어요.

$ ./singlechar
a
a
abcdefg
abcdefg

파이프를 사용하면, 아래와 같은 결과를 확인할 수 있어요.

$ ls
singlechar  singlechar.c  template.c
$ ls | ./singlechar
singlechar
singlechar.c
template.c

2. 문자열로 출력하고 입력받기

이번에는, 두 글자 이상을 한번에 출력하고 입력받는 방법에 대해 알아볼게요. 자주 사용하던 printfscanf함수의 이야기에요.

printf

int printf(const char * restrict format, ...);

printf는 가변 인자 함수로, 첫 번째 인자로 제어 문자열을 받고 그 이후에는 변환 명세들에 맞게 인자를 전달해 사용해요.
변환 명세란, 제어 문자열 내에서 %부터 변환 문자까지의 부분을 말해요.

제어 문자열을 화면에 출력할 때, 변환 명세를 만나면 대응하는 인자를 변환 명세에 맞게 적절히 형 변환을 거쳐 화면에 출력해요.

int age = 20;
char *name = "홍길동";
printf("%s의 나이는 %d살 입니다.\n", name, age);

만약 변환 명세의 수보다 인자가 많을 경우, 여분의 인자는 무시해요. 반대로, 변환 명세의 수보다 인자가 적을 경우, 시스템 종속적인 동작을 하게 돼요.

변환 명세

변환 명세는 아래와 같은 형식으로 작성해요.

%[플래그][폭][.정밀도][형변환자]변환문자

변환문자는 생략할 수 없고, 나머지 항목들은 선택적으로 작성할 수 있어요.

변환 문자

변환 문자의 종류는 아래와 같아요.

변환 문자출력
c문자 (char 형 값)
d, i10진 정수
u부호가 없는 10진 정수
o부호가 없는 8진 정수
x, X부호가 없는 16진 정수
f, F부동 소수점 표기법의 실수 (ex : 7.123000)
e, E지수 표기법의 실수 (ex : 7.123000+e00, 7.123000+E00)
a, A(C99) 16진 표기법의 실수 (ex : 0x7.123p+20, 0X7.123p+20)
g, Gg: e형식과 f형식 중 짧은 것 / G: E형식과 F형식 중 짧은 것
s널('\0') 로 끝나는 문자열
p포인터 값의 16진 정수 (주소값)
n특정 값으로 출력되지 않고, 대응되는 정수형 포인터 인자에 현재까지 출력된 문자의 개수를 저장
%%문자를 출력함 (이스케이핑). 대응되는 인자 X

플래그

플래그를 변환 명세의 출력 형식을 변경할 수 있어요.
변환 명세를 통해 인자를 출력할 때, 인자가 출력되는 공간을 '필드' 라고 해요.

플래그기능
-필드에서 인자가 왼쪽 정렬되어 출력됨
+숫자 앞에 +나 -의 부호를 항상 붙여 출력
공백숫자를 오른쪽 정렬로 출력할 때, 남는 공간을 공백으로 채워 출력
0숫자를 오른쪽 정렬로 출력할 때, 남는 공간을 0으로 채워 출력
#8진수는 앞에 0, 16진수는 앞에 0x를 출력

필드의 크기 (문자 수)를 지정해요. 폭은 양의 정수값을 직접 적거나, *을 사용해 printf의 인자로 받을 수 있어요.

int i = 10;
printf("i = %4d\n", i);
printf("i = %*d\n", 4, i);
  10
  10

정밀도

정밀도는 . 뒤에 작성해요. 음이 아닌 정수값을 직접 기입하거나, *을 사용해 printf의 인자로 지정할 수 있어요.

정밀도는 변환 문자에 따라 다른 역할을 해요.

  • 실수 변환 문자 (f, F, e, E, a, A) : 소수점 이하의 자릿수
  • g, G : 최대 유효 숫자
  • s : 문자열에서 출력할 문자의 최대 개수
char *str = "Hello World!";
float f = 1.3f;
double d = 2.4;

printf("%.5s\n", str);
printf("f = %.2f\n", f);
printf("d = %.5G\n", d);
Hello
f = 1.30
d = 2.4

💡 최대 유효 숫자?
g, G 변환문자의 경우, 최대 유효 숫자를 지정해요. 다른 실수 변환 문자의 경우 출력할 소숫점 이하 자릿수를 정의해, 직접적으로 소수 부분의 길이가 달라지지만 g, G의 경우는 .5를 주더라도 2.4와 같은 소수는 그대로 2.4로 나오는 것을 볼 수 있어요. 이전에 부동 소수점 형식의 자료형은 소수점 이하 n째 자리까지 정밀하다는 이야기를 했는데, 그 개념을 떠올리면 될 것 같아요. (어디까지나 추측이에요! 조금 더 알아보고 오겠습니다.)

형변환자

형변환자를 사용해, 인자의 메모리 크기를 지정할 수 있어요.

형변환자뒤에 올 수 있는 변환 문자변환 형
hhd, i, o, u, x, Xsigned/unsigned char
hd, i, o, u, x, Xsigned/unsigned short
ld, i, o, u, x, Xsigned/unsigned long
lld, i, o, u, x, Xsigned/unsigned long long
zd, i, o, u, x, Xsize_t
La, A, e, E, f, F, g, Glong double

scanf

int scanf(const char * restrict format, ...);

scanf는 printf와 마찬가지로 가변함수로, 첫 번째 인자로 제어 문자열을 받아요.

표준 입력으로부터 변환 명세대로 읽어 대응 인자에 배정하는 기능을 해요. 이 때문에, 대응 인자는 포인터여야 해요.

만약 제어 문자열에 변환 명세가 아닌 일반 문자가 있을 경우, 입력 스트림으로부터 동일한 문자를 제거해요.

char s[10] = {};
scanf("name : %s", s);
printf("\nHello, %s\n", s);
$ ./scanf_prog
name : Minsu Kim

Hello, Minsu Kim

변환 명세

scanf에서 사용하는 변환 명세는 아래와 같은 양식으로 작성해요.

%[*][폭][형변환자]변환문자

변환문자는 필수적으로 작성해야 하고, 나머지의 경우 선택적으로 사용할 수 있어요.

변환문자

변환문자입력값의 형태대응 인자의 자료형
c공백을 포함한 모든 문자char *
d, i10진 정수 (부호는 옵션)int *
u10진 정수 (부호는 옵션)unsigned int *
o8진 정수 (부호는 옵션)unsigned int *
x16진 정수 (부호는 옵션)unsigned int *
a, e, f, g실수 (부호는 옵션)실수 포인터
s공백 없는 문자열char *
p보통 16진 정수를 받지만, 시스템에 따라 다름void *
n지금까지 읽은 문자 개수를 대응 인자에 배정함. 입력 스트림의 내용을 읽어들이진 않음.int *
%입력 스트림에서 % 문자를 읽음 (이스케이핑)없음
[]아래에서 설명char *
변환문자 []

[]는 특수한 변환문자로, '스캔 집합'이라 부르는 원하는 문자들로만 구성된 문자열을 읽어들여요. 입력 스트림에서 스캔집합 이외의 문자가 나올 때까지 읽어들인 후, 대응하는 char * 형 인자에 배정해요.

char s[10] = {};
scanf("%[0-9a-fA-F]", s);	// 숫자와 a~f의 알파벳 대소문자만 읽음.

[] 안의 첫 번째 문자가 ^일 경우, 괄호 내의 문자를 제외한 나머지 문자들이 스캔 집합이 돼요.

scanf("%[^ \n\t]", s);	// 공백과 개행 문자. 탭 문자를 제외한 나머지 문자를 읽음.

*

입력 스트림의 내용을 지워요.

int a, b, c;
scanf("%d %d %*d, %d", &a, &b, &c);
printf("a = %d, b = %d, c = %d\n", a, b, c);
10 20 30 40
a = 10, b = 20, c = 40

10과 20은 각각 a와 b에 저장되었고, 30은 *을 사용해 무시했어요. 따라서, c에는 40이 저장되었어요.

입력 스트림에서 읽어들일 필드의 최대 문자 길이를 지정해요.

int num;
char str[5] = {};
scanf("%3d %4c", &num, str);
printf("num = %d\n%s\n", num, str);
1234567890
num = 123
4567

형변환자

printf와 마찬가지로, 형변환자를 사용해 인자의 크기를 지정할 수 있어요.

형변환자뒤에 올 수 있는 변환 문자인자의 자료형
hhd, i, o, u, x, X, nsigned/unsigned char *
hd, i, o, u, x, X, nsigned/unsigned short *
ld, i, o, u, x, X, nsigned/unsigned long *
la, A, e, E, f, F, g, Gdouble *
lld, i, o, u, x, Xsigned/unsigned long long *
zd, i, o, u, x, X, nsize_t *
La, A, e, E, f, F, g, Glong double *

의도한 자료형에 맞는 변환 문자를 사용해야, 제대로 값을 받을 수 있어요.

int i;
float f;
double d;

scanf("%d", &i);	// 1234 입력
printf("%d", i);	// 1234
scanf("%d", &i);	// 12.34 입력
printf("%d", i);	// 12

// .34 부분은 입력 스트림에서 읽어지지 않은 채 남아있어요.
// 이는 %d 형식이 12까지만 읽고 .에서 오류가 나 종료했기 때문이에요.
// 따라서, *를 사용해 입력 스트림에서 ".34"를 비워줄게요.
scanf("%*f");

scanf("%f", &f);	// 12.34 입력
printf("%f", f);	// 12.340000
scanf("%d", &i);	// 1234 입력
printf("%d", i);	// 0.00000000

scanf("%lf", &d);	// 12.34 입력
printf("%f", d);	// 12.340000
profile
새내기 대학생 개발자에요 :D

0개의 댓글