`printf()` 함수란?
`printf()` 함수는 C 프로그래밍에서 데이터를 출력하기 위해 가장 널리 사용되는 함수이다. 형식 지정자를 활용하여 다양한 데이터 타입을 출력할 수 있으며, 콘솔 기반 출력의 기본 도구이다.
형식 문자열과 형식 지정자
`printf()` 함수는 형식 문자열을 입력받아 처리한다. 형식 문자열을 일반 텍스트와 형식 지정자로 구성되며, 형식 지정자는 `%d`, `%f`, `%s` 등으로 데이터 타입을 정의한다.
가변 인자의 처리 방법
`printf()`는 가변 인자를 처리하기 위해 `stdarg.h` 에 정의된 `va_list`, `va_start`, `va_arg`, `va_end`를 사용한다. 이를 통해 함수 호출 시 전달된 인자들을 순차적으로 읽어 형식 지정자에 따라 처리한다.
`stdarg.h`의 사용법
가변 인자를 처리하기 위해 반드시 알아야 할 라이브러리이다. `va_list`를 선언하고, `va_start`로 초기화한 뒤 `va_arg`로 각 인자를 읽어보는 방식이다.
문자열 처리와 출력 스트림 개념
문자열을 구성하고 이를 출력 스트림에 전달하는 과정을 이해해야 한다. 저수준 출력 함수인 `putchar()`를 활용해 출력 스트림을 관리할 수 있다.
함수 구현
Step 1 : 기본 출력 함수 만들기
가장 간단한 형태로 문자열을 출력하는 함수부터 시작해 보겠다.
- 문자열을 하나씩 읽어 출력하는 방식으로 작동한다.
- `putchar()`를 활용해 각 문자를 출력한다.
#include <stdio.h>
void my_printf(const char *str) {
while(*str) { // 가리키는 문자가 NULL이 아닐 때까지
putchar(*str); // 출력
str++; // 다음 문자로 이동
}
}
int main(void) {
my_printf("Hello, World!\n");
return 0;
}
이 함수는 문자열만 출력할 수 있고, 서식 지정(format specifiers, e.g., `%d`, `%s`)을 지원하지 않는다.
Step 2 : 포맷 처리 없이 숫자 출력
`printf()`는 숫자도 출력할 수 있다. 이를 위해 숫자를 문자열로 변환하는 기능을 추가한다.
- 음수 처리 및 재귀적인 숫자 출력 로직을 작성한다.
- 각 자리 숫자를 `putchar()`를 통해 출력한다.
#include <stdio.h>
void print_number(int num) {
if(num < 0) {
putchar('-');
num = -num;
}
if(num / 10) {
print_number(num / 10);
}
putchar('0' + (num % 10));
}
int main(void) {
print_number(12345);
putchar('\n');
print_number(-6789);
return 0;
}
음수인 경우 `-`을 출력하고 양수로 변환한 후 숫자를 출력한다.
예를 들어, `12345`를 처리할 때
- `print_number(1234)` -> `print_number(123)` -> `print_number(12)` -> `print_number(1)`로 재귀 호출된다.
Step 3 : 간단한 %d 포맷 처리
숫자와 문자열 출력을 구분하여 처리하는 간단하나 버전을 구현한다.
- `stdarg.h`의 `va_list`를 사용해 가변 인자를 읽어온다.
- `%d` 형식 지정자를 만나면 정수를 출력한다.
#include <stdio.h>
#include <stdarg.h>
void print_number(int num) {
if(num < 0) {
putchar('-');
num = -num;
}
if(num / 10) {
print_number(num / 10);
}
putchar('0' + (num % 10));
}
void my_printf(const char *format, ...) {
va_list args;
va_start(args, format);
while(*format) {
if(*format == '%' && *(format + 1) == 'd') {
int num = va_arg(args, int); // 가변 인자에서 정수 가져오기
print_number(num);
format += 2;
} else {
putchar(*format);
format++;
}
}
va_end(args);
}
int main(void) {
my_printf("Number : %d\n", 42);
return 0;
}
format 문자열을 한 문자씩 읽어 처리하고 현재 문자가 `'%'`이고 다음 문자(`*(format + 1)`)가 `'d'`라면
- `va_arg(args, int)`를 사용해 가변 인자 리스트에서 정수를 가져온다.
- `print_number`를 호출해 해당 정수를 출력한다.
- `format += 2`로 포맷 문자열에서 `%d`를 건너뛴다.
`%d`가 아닌 일반 문자는 그대로 출력하고 `*format`이 NULL이면 종료한다.
Step 4 : 다중 포맷 처리
이제 문자열 포맷인 '%s'를 추가한다.
void my_printf(const char *format, ...) {
va_list args;
va_start(args, format);
while (*format) {
if (*format == '%' && *(format + 1) == 'd') {
int num = va_arg(args, int);
print_number(num);
format += 2;
} else if (*format == '%' && *(format + 1) == 's') {
const char *str = va_arg(args, const char *);
my_printf(str);
format += 2;
} else {
putchar(*format);
format++;
}
}
va_end(args);
}
현재 문자가 `'%'`이고 다음 문자가 `'s'`인 경우
- `va_arg(args, const char *)`로 가변 인자에서 문자열 포인터를 가져온다.
- `my_printf(str)`를 호출하여 해당 문자열을 출력한다.
- 문자열을 출력하기 위해 `my_printf` 자신을 재귀 호출한다.
- `format += 2`로 `%s`를 건너뛴다.
오류 처리 및 예외 상황
기본적인 틀을 이렇게 잡고 잘못된 형식 문자열이나 부족한 인자 개수에 대해 오류를 감지하고 적절히 처리해야 한다.
'C언어' 카테고리의 다른 글
[C언어] 개행 문자 (0) | 2024.12.13 |
---|---|
[C언어] qsort() 함수란? (0) | 2024.11.18 |
srand() 함수란? (0) | 2024.11.15 |
[C언어] OOP 공부 (2) (1) | 2024.11.13 |
티스토리 기본모드 수식 삽입 (2) | 2024.11.13 |