표준 입출력은 함수 이름보다 stdout/stderr 구분과 입력 방식 선택이 먼저입니다.
숏컷 코드
printf("count=%d\n", count);
fprintf(stderr, "error: %s\n", msg);문법
표준 입출력은 stdin, stdout, stderr 세 스트림을 중심으로 움직입니다. 함수 선택은 결국 “어디로 보내나”와 “어떤 단위로 읽나”를 같이 보는 문제입니다.
stdin: 입력stdout: 정상 출력stderr: 오류와 진단 출력
실전에서는 이 구분이 중요합니다.
- 프로그램 결과는
stdout - 실패 원인과 디버그 메시지는
stderr
그래야 리디렉션이나 파이프를 써도 결과와 오류가 섞이지 않습니다.
char line[128];
if (fgets(line, sizeof(line), stdin) == NULL) {
return 1;
}입력 방식
fgets를 기본값처럼 쓰는 이유는 단순합니다.
- 버퍼 크기를 알고 있다
- 공백 포함 한 줄을 읽는다
- 마지막에
\0이 붙는다
반대로 scanf("%s", buf)는 공백에서 끊기고, 길이 제한을 빼먹으면 버퍼 오버플로우로 바로 이어집니다.
형식 입력 자체가 나쁜 것은 아니지만, scanf를 쓴다면 반환값 검사를 같이 붙여야 합니다.
int n;
if (scanf("%d", &n) != 1) {
fprintf(stderr, "정수 입력 실패\n");
return 1;
}여기서 중요한 것은 입력 함수가 아니라 반환값 검사입니다.
- 원하는 개수만큼 읽었는지 확인
- 실패하면 변수를 믿지 않는다
- 필요하면 버퍼를 비우고 다시 읽는다
함수 선택
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) {
putchar(ch);
}반환값을 int로 받는 이유는 EOF를 구분해야 하기 때문입니다. char로 받으면 EOF와 실제 문자 값이 섞일 수 있습니다.
%d:int%ld:long%lld:long long%f:printf에서double%lf:scanf에서double%zu:size_t%p: 포인터 주소,(void *)로 캐스팅해서 넘기는 습관이 안전
선택 기준
- 결과 출력은
stdout - 오류 출력은
stderr - 줄 입력은
fgets scanf는 반환값 확인- 문자 스트림 처리는
getchar - 포맷 출력은 형식 지정자와 실제 타입을 같이 본다
주의할 점
printf 형식 지정자와 실제 인자 타입이 다르면 "보기만 이상한" 수준이 아니라 정의되지 않은 동작입니다. 특히 size_t, long long, 포인터 출력에서 습관적으로 %d를 쓰지 않는 쪽이 좋습니다.
참고 링크
2 sources