C - 1.6. 문자 입출력을 이용한 프로그램1

목차

1. 입력받은 문자 개수 세기

문자 입출력을 이용해 만들어 볼 다음 프로그램은 문자 개수를 세는 프로그램이다.

#include <stdio.h>

/* 입력에 문자가 몇 개 있는지를 세어 준다 */
main() {
    int count;

    count = 0;
    while (getchar() != EOF) {
        ++count;
    }
    printf("%d\n", count);
}
#include <stdio.h>

/* 입력에 문자가 몇 개 있는지를 세어 준다 */
main() {
    int count;

    count = 0;
    while (getchar() != EOF) {
        ++count;
    }
    printf("%d\n", count);
}
#include <stdio.h>

/* 입력에 문자가 몇 개 있는지를 세어 준다 */
main() {
    int count;

    count = 0;
    while (getchar() != EOF) {
        ++count;
    }
    printf("%d\n", count);
}
#include <stdio.h>

/* 입력에 문자가 몇 개 있는지를 세어 준다 */
main() {
    int count;

    count = 0;
    while (getchar() != EOF) {
        ++count;
    }
    printf("%d\n", count);
}

기본 원리는 1.5에서 다루었던 것과 같다. EOF가 나올 때까지 문자 입력을 받으면서 몇 개나 입력받았는지 세는 것이다.

그런데 여기서 우리가 보지 못했던 연산자가 또 하나 나왔다. 바로 ++count 에서 나온 전위 증감 연산자이다. ++countcount의 값을 1 증가시킨 후 변수의 값을 사용한다. 이는 count=count+1 과 같은 기능이다. 하지만 ++count 가 더 간결하고 빠르기 때문에 1 을 증가시킬 경우 증감 연산자를 자주 쓴다.

비슷한 방식으로 변수에서 1을 감소시키는 --count 가 있고 또 조금은 다른 방식으로 동작하는 count++ count-- 후위 증감 연산자가 있다. 이는 연산자에 관해 다룰 때 추후 더 자세히 다룰 것이다.


하지만 이론적으로는 전위 연산자 ++i의 승리이다. 후위 연산자 i++의 경우 먼저 변수의 값을 사용한 후에 변수의 값을 1 증가시켜 준다. 가령 arr[i++]=3 과 같은 코드가 있다면 먼저 arr[i]=3 을 처리해 준 후 i를 1 증가시켜 주는 것이다. 하지만 arr[++i]의 경우 그냥 i를 1 증가시켜 준 후 arr[i] 에 대한 처리를 하면 된다.

따라서 후위 연산자의 경우 i의 값을 저장할 임시 공간을 만들어야 하기 때문에 후위 연산자가 전위 연산자에 비해 이론적으로는 속도가 떨어진다.

참고 : https://stackoverflow.com/questions/24901/is-there-a-performance-difference-between-i-and-i-in-c


2. 다른 방식으로 짜보기

이때 int의 범위는 일반적으로 2^31-1, 약 21억까지이므로 문자 개수를 셀 때 있어서 부족할 일은 별로 없을 것이다. 그러나 만약 어떤 엄청나게 큰 입력이 있어서 그 범위를 초과한다면 double형을 사용하여 정확도를 조금 포기하는 대신 훨씬 큰 범위를 표현하게 할 수 있을 것이다.

다른 종류의 반복문인 for문을 사용하여 그러한 코드를 짜 보자.

#include <stdio.h>

/* for문, double형을 이용해 입력받은 문자 개수를 세는 코드 */
main() {
	double count;

	for (count = 0; getchar() != EOF; ++count);
	/* 초기화/테스트/증가로 필요한 모든 동작이 이루어지므로 for문의 body는 없어도 된다. */
	printf("%.0f\n", count);
}
#include <stdio.h>

/* for문, double형을 이용해 입력받은 문자 개수를 세는 코드 */
main() {
	double count;

	for (count = 0; getchar() != EOF; ++count);
	/* 초기화/테스트/증가로 필요한 모든 동작이 이루어지므로 for문의 body는 없어도 된다. */
	printf("%.0f\n", count);
}
#include <stdio.h>

/* for문, double형을 이용해 입력받은 문자 개수를 세는 코드 */
main() {
	double count;

	for (count = 0; getchar() != EOF; ++count);
	/* 초기화/테스트/증가로 필요한 모든 동작이 이루어지므로 for문의 body는 없어도 된다. */
	printf("%.0f\n", count);
}
#include <stdio.h>

/* for문, double형을 이용해 입력받은 문자 개수를 세는 코드 */
main() {
	double count;

	for (count = 0; getchar() != EOF; ++count);
	/* 초기화/테스트/증가로 필요한 모든 동작이 이루어지므로 for문의 body는 없어도 된다. */
	printf("%.0f\n", count);
}

double형은 약 10^300 까지 나타낼 수 있으므로 위의 코드에서의 count변수의 오버플로우는 거의 걱정하지 않아도 될 것이다. 그리고 double 형의 printf 출력 포맷 또한 %f 임을 알아 두자.

또한 for문이 중괄호 body가 없어도 잘 동작한다는 것을 알아 두자.

그런데 만약 이 프로그램에 입력이 아무것도 안 들어온다면 어떻게 될까? 입력에 아무 문자도 없고 바로 EOF가 입력되는 것이다. 그때도 프로그램은 정상적으로 0을 출력한다. count를 증가시키는 부분이 전혀 실행되지 않기 때문이다.

이렇게 for문 혹은 while문의 조건이 만족되지 않으면 반복문의 몸체(중괄호 내부)는 전혀, 한번도 실행되지 않고 종료된다. 아무것도 입력되지 않는 것과 같은 이런 코너케이스를 처리하는 것은 프로그램을 탄탄하게 짤 때 매우 중요하다.

3. 줄 개수 세기

다음에 짜 볼 코드는 입력 속에 있는 개행을 세어 주는 코드이다. 입력을 끝까지 받으면서 개행 문자가 들어오면 세어 주는 단순한 논리로 작동한다.

#include <stdio.h>

/* 입력에 있는 줄 수 세기 */
main() {
	int c, line_num;

	line_num = 0;
	while ((c = getchar()) != EOF) {
		if (c == '\n') {
			line_num++;
		}
	}
	printf("%d\n", line_num);
}
#include <stdio.h>

/* 입력에 있는 줄 수 세기 */
main() {
	int c, line_num;

	line_num = 0;
	while ((c = getchar()) != EOF) {
		if (c == '\n') {
			line_num++;
		}
	}
	printf("%d\n", line_num);
}
#include <stdio.h>

/* 입력에 있는 줄 수 세기 */
main() {
	int c, line_num;

	line_num = 0;
	while ((c = getchar()) != EOF) {
		if (c == '\n') {
			line_num++;
		}
	}
	printf("%d\n", line_num);
}
#include <stdio.h>

/* 입력에 있는 줄 수 세기 */
main() {
	int c, line_num;

	line_num = 0;
	while ((c = getchar()) != EOF) {
		if (c == '\n') {
			line_num++;
		}
	}
	printf("%d\n", line_num);
}

이때 개행을 나타낼 때 쓰는 이스케이프 문자인 \n 이 int형 변수인 c와 비교됨을 주의깊게 보자. 작은따옴표로 나타낸 문자는 컴파일러에서 쓰는 character set에서 대응되는 특정 숫자와 같다. 이를 문자 상수라고 부르며, 흔히 아스키 코드 셋으로 나타낸다. 가령 대문자 알파벳 A는 아스키 코드로 65이다.