함수 printf는 C 표준 라이브러리에서 가장 일반적으로 사용되는 포맷 출력 함수 중 하나입니다. 이 함수는 포맷 문자열을 분석하여 지정된 유형에 따라 데이터를 표준 출력 스트림(stdout)으로 전달합니다.
가변 인수의 구현 원리
printf 함수는 가변 인수 목록(variadic arguments)을 사용하며, <stdarg.h> 헤더 파일에서 정의된 매크로를 통해 인수에 접근할 수 있습니다. 다음은 간단한 printf 호환 함수를 구현한 예제입니다:
#include <stdio.h>
#include <stdarg.h>
void my_print(const char *fmt, ...) {
va_list args;
va_start(args, fmt); // 인수 목록 초기화
vfprintf(stdout, fmt, args); // vfprintf 호출
va_end(args); // 정리
}
포맷 문자열의 파싱 과정
실행 시 printf는 포맷 문자열을 문자 단위로 스캔하여 % 기호로 시작하는 플레이스홀더를 식별하고, 그 후의 유형 문자(d, s, f 등)에 따라 해당 유형의 데이터를 추출하여 변환합니다.
| 형식 지정자 | 데이터 유형 | 기본 동작 |
|---|---|---|
| %d | int | 10진수 문자열로 변환 |
| %s | char* | 메모리에서 복사하여 출력 버퍼로 전송 |
| %p | void* | 주소를 16진수로 표현 |
사용자 정의 형식 지정자의 구현
C99 표준에서는 size_t 타입을 출력하기 위한 %zu 형식 지정자가 도입되었습니다. 이를 사용하면 크기를 올바르게 출력할 수 있습니다.
#include <stdio.h>
int main() {
size_t sz = 512;
printf("Size: %zu\n", sz); // %zu 사용
return 0;
}
%m 형식 지정자의 구현
%m 형식 지정자는 현재 errno 값에 해당하는 오류 메시지를 출력합니다. 이는 strerror(errno)와 동일한 효과를 제공합니다.
#include <stdio.h>
#include <errno.h>
int main() {
FILE *fp = fopen("/nonexistent/file.txt", "r");
if (!fp) {
printf("Error: %m\n"); // %m 사용
}
return 0;
}
출력 제어의 고급 기능
포맷 지정자를 사용하여 폭(width), 정밀도(precision) 및 정렬(alignment)을 조절할 수 있습니다. 예를 들어:
printf("|%10s|\n", "Hello"); // 오른쪽 정렬, 폭 10
printf("|%-10s|\n", "Hello"); // 왼쪽 정렬, 폭 10
printf("|%8.2f|\n", 3.14159); // 총 폭 8, 소수점 아래 2자리
이 코드들은 각각 문자열과 부동 소수점 숫자에 대한 다양한 출력 형식을 보여줍니다.