C언어

[C언어] printf 함수 구현

_minit 2024. 12. 19. 15:59

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.hva_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를 건너뛴다.

 

오류 처리 및 예외 상황

기본적인 틀을 이렇게 잡고 잘못된 형식 문자열이나 부족한 인자 개수에 대해 오류를 감지하고 적절히 처리해야 한다.

 

728x90

'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