프로그래밍

입력과 출력

사용자와 값을 주고받지 못하는 프로그램은
매번 같은 결과만 출력합니다.

학습 목표

  1. 서식 지정자를 써서 변수의 값을 원하는 형식으로 화면에 출력할 수 있다.
  2. 이스케이프 문자로 줄을 바꾸고 탭으로 열을 맞출 수 있다.
  3. 키보드로 입력받은 값을 변수에 저장할 수 있고, 입력받을 때 &를 붙이는 이유를 설명할 수 있다.
  4. 서식 지정자와 자료형이 맞지 않을 때 무슨 일이 일어나는지 알고, 자주 하는 실수를 피할 수 있다.

화면에 출력하기

문장 속 원하는 위치에 값을 넣어 출력합니다.

변수에 값을 저장해 두기만 해서는 사람이 볼 수 없습니다. 저장된 값을 보려면 화면에 출력해야 합니다. 그 일을 하는 것이 printf입니다.

int score = 90;
double height = 170.52;

printf("점수는 %d점입니다.\n", score);        // 점수는 90점입니다.
printf("키는 %.1f cm입니다.\n", height);      // 키는 170.5 cm입니다.
printf("%d\t%d\t%d\n", 1, 2, 3);              // 1  2  3   (탭만큼 띄워 출력된다)

서식 지정자(format specifier)

%d처럼 퍼센트 기호로 시작하는 표시입니다. 「이 위치에 값을 하나 넣어라, 그 값의 자료형은 이것이다」라는 뜻이고, 넣을 값은 쉼표 뒤에 순서대로 적습니다.

서식 지정자 대응하는 자료형 보기
%d int 정수 printf("%d", 90) → 90
%f double 실수 printf("%f", 1.5) → 1.500000
%c char 문자 하나 printf("%c", 'A') → A
%s 문자열 printf("%s", "안녕") → 안녕

지금은 문자열을 "안녕"처럼 큰따옴표로 코드에 바로 쓴 것만 출력합니다. 문자열을 변수에 저장하려면 int·double·char만으로는 부족합니다.

직접 입력할 수 없는 문자를 나타내는 이스케이프 문자(escape sequence)

줄바꿈은 따옴표 안에서 엔터를 눌러 넣을 수 없습니다. 그래서 역슬래시로 시작하는 두 글자로 그 문자를 나타냅니다.

  • \n — 줄을 바꾼다. 붙이지 않으면 다음 출력이 바로 뒤에 이어진다.
  • \t — 다음 탭 위치까지 띄운다. 표처럼 열을 맞출 때 쓴다.
  • \\ — 역슬래시 자체. \ 하나만 쓰면 뒤 글자와 묶여 이스케이프 문자로 읽힌다.
  • \" — 큰따옴표 자체. 그냥 쓰면 문자열이 거기서 끝난다.

스스로 확인

printf("A");와 printf("B");를 이어 쓰면 화면에 어떻게 나올까요? 두 줄일까요, 한 줄일까요?

출력 형식 맞추기

같은 값도 출력 폭을 맞추면 읽기 쉬워집니다.

서식 지정자에는 출력 폭과 소수점 아래 자릿수를 함께 적을 수 있습니다. 여러 줄을 나란히 출력할 때 오른쪽 끝이 맞아 표처럼 읽힙니다.

printf("[%5d]\n", 42);        // [   42]   폭 5에 맞춰 오른쪽으로 정렬한다
printf("[%-5d]\n", 42);       // [42   ]   -를 붙이면 왼쪽으로 정렬한다
printf("[%.2f]\n", 3.14159);  // [3.14]    소수점 아래 두 자리까지
printf("[%%]\n");             // [%]       퍼센트 기호 자체를 출력할 때

폭은 「최소」입니다

%5d로 여섯 자리 수를 출력하면 잘리지 않고 여섯 자리가 모두 출력됩니다. 값이 폭보다 짧을 때만 남는 부분을 공백으로 채웁니다.

%.2f는 반올림합니다

화면에 보이는 값만 바뀝니다. 변수에 저장된 값 자체는 그대로입니다. 소수점 아래 셋째 자리에서 반올림하므로 (int)처럼 소수점 아래를 잘라 버리는 것과도 다릅니다.

퍼센트 기호 자체를 출력하려면 %%처럼 두 번 씁니다. 하나만 쓰면 뒤 글자와 묶어 서식 지정자로 읽어 버립니다.

스스로 확인

printf("[%8.3f]", 3.14159);는 대괄호 안에 몇 글자가 들어갈까요? 세어 보고 확인해 보세요.

키보드로 입력받기

입력받을 때는 값이 아니라 변수가 있는 위치를 알려 주어야 합니다.

scanf는 키보드로 입력한 값을 읽어 변수에 저장합니다. 쓰는 형식은 printf와 비슷하지만 변수 이름 앞에 &가 붙습니다.

int age;
printf("나이를 입력하세요: ");
scanf("%d", &age);            // age가 있는 위치(주소)를 알려 준다
printf("내년이면 %d살입니다.\n", age + 1);

&를 붙이는 이유

printf는 값을 출력하기만 하면 되므로 값만 넘기면 됩니다. scanf는 반대로 변수에 새 값을 저장해야 합니다. 그러려면 「변수에 어떤 값이 있는지」가 아니라 「그 변수가 메모리의 어디에 있는지」를 알려 주어야 합니다.

&age는 age가 메모리의 어디에 있는지를 나타내는 값이고, 이 값을 age의 주소라고 합니다. 주소는 뒤에 포인터를 배울 때 자세히 다루므로, 지금은 「scanf로 입력받을 때는 &를 붙인다」로 기억해 두면 됩니다.

한 번에 여러 값을 입력받을 수도 있습니다. 사용자가 공백이나 엔터로 구분해 입력하면 순서대로 저장됩니다.

int a, b;
scanf("%d %d", &a, &b);       // 값 두 개를 한 번에 입력받는다
printf("합은 %d입니다.\n", a + b);

안내 문구는 직접 출력합니다

scanf는 아무 안내 없이 입력을 기다리기만 합니다. 앞에 printf("나이를 입력하세요: ")를 두지 않으면 화면이 멈춘 것처럼 보입니다.

스스로 확인

scanf("%d", age);처럼 &를 빠뜨리면 무슨 일이 일어날까요? 컴파일은 될까요?

자주 하는 실수 세 가지

세 가지 모두 컴파일은 되기 때문에 찾기가 어렵습니다.

1. 서식 지정자와 자료형이 맞지 않는다

double 값을 %d로 출력하면 엉뚱한 수가 나옵니다. 값을 잘못 읽는 것이라 경고가 뜨더라도 실행은 되고, 오류 대신 틀린 결과를 얻게 됩니다. 실수는 %f, 정수는 %d로 자료형에 맞춰 씁니다. 입력받을 때는 하나가 다릅니다. scanf로 double 값을 입력받으려면 %f가 아니라 %lf를 씁니다. %f로 입력받으면 역시 오류 없이 엉뚱한 값이 저장됩니다.

2. &를 빠뜨린다

컴파일러가 경고만 하고 넘어가는 경우가 있습니다. 그러면 실행 도중 프로그램이 갑자기 비정상 종료되거나, 엉뚱한 메모리 위치에 값을 저장합니다. 경고를 무시하지 않는 것이 이 실수를 일찍 잡는 방법입니다.

3. 남은 엔터가 다음 입력으로 읽힌다

정수를 입력받은 뒤 문자를 입력받으면 문자를 입력할 틈도 없이 넘어갑니다. %d는 숫자만 읽고 사용자가 누른 엔터, 곧 줄바꿈 문자를 남겨 두는데, %c는 그 줄바꿈 문자를 문자 하나로 읽어 버리기 때문입니다.

int score;
char grade;

scanf("%d", &score);
scanf(" %c", &grade);         // %c 앞의 공백 하나가 남은 줄바꿈 문자를 건너뛴다

printf("%d점, 등급 %c\n", score, grade);

%c 앞에 공백 하나를 두면 「남은 공백과 줄바꿈을 건너뛰고 읽어라」라는 뜻이 되어 해결됩니다. %d나 %lf 앞에는 필요 없습니다. 이 둘은 앞에 남은 공백과 줄바꿈을 알아서 건너뜁니다.

입력받아 계산하고 출력하기

입력 · 계산 · 출력을 이으면 온전한 프로그램이 됩니다. 아래는 그대로 실행해 볼 수 있는 전체 코드입니다.

#include <stdio.h>

int main(void) {
    int kor, eng;

    printf("국어와 영어 점수를 입력하세요: ");
    scanf("%d %d", &kor, &eng);

    printf("합계 %d점, 평균 %.1f점\n", kor + eng, (kor + eng) / 2.0);
    return 0;
}

평균을 구할 때 2가 아니라 2.0으로 나눈 것을 눈여겨보세요. 둘 다 정수이면 몫만 남아 소수점 아래가 사라집니다. 앞에서 본 정수끼리의 나눗셈 함정입니다.

스스로 확인

「입력받아 계산하고 출력하기」의 프로그램에 85와 90을 입력하면 평균은 얼마로 출력될까요? 평균 식을 (double)(kor + eng) / 2로 쓸 때와 (kor + eng) / 2로 쓸 때 각각 어떤 값이 계산될까요? 앞에 붙은 (double)은 강제 형 변환으로, 바로 뒤의 값을 실수로 바꿉니다.

핵심 정리

서식 지정자

%d 정수, %f 실수, %c 문자 하나, %s 문자열. 값의 자료형에 맞춰 쓴다. 맞지 않으면 오류가 아니라 틀린 값이 나온다. scanf로 double 값을 입력받을 때만 %lf다.

이스케이프 문자

\n 줄바꿈, \t 탭(열 맞추기). \n이 없으면 다음 출력이 바로 뒤에 이어진다.

scanf와 &

입력받을 때는 값이 아니라 변수가 있는 위치(주소)를 알려 주어야 하므로 &를 붙인다. 안내 문구는 printf로 직접 출력한다.

남은 엔터

정수를 입력받은 뒤 문자를 입력받으면 남은 줄바꿈 문자가 먼저 읽힌다. %c 앞에 공백 하나를 두면 건너뛴다.

확인 퀴즈

배운 내용을 확인해 봅시다.

1. printf("[%.2f]", 3.14159);의 결과는?

2. scanf에서 변수 앞에 &를 붙이는 이유는?

3. 정수를 입력받은 뒤 scanf("%c", &ch);로 문자를 입력받으면 입력할 틈도 없이 넘어간다. 왜인가?