42Seoul
ft_printf.c
Lord DEVader
2022. 12. 21. 13:52
program name | libftprintf.a |
External functs. | malloc, free, wirte, va_start, va_arg, va_copy, va_end |
Description | 오리지널 printf()를 흉내내는 ft_printf() 구현 |
prototype | ft_printf(const char *, ...); |
- Mandatory
- 오리지널 printf()의 버퍼는 구현하지 않는다.
- cspdiuxX% 옵션을 handle 한다.
- %c : character 출력
- %s : string 출력
- %p : void * 포인터 출력
- %d : 10진법 숫자 출력
- %i : 10진법 정수 출력
- %u : unsigned 10진법 숫자 출력
- %x : 16진법 숫자를 소문자로 출력
- %X : 16진법 숫자를 대문자로 출력
- %% : '%' 출력
- 필요 개념
- va_list
- : 가변인자. 변수가 몇개 들어올지 모를때 사용할 수 있다.
- va_list 라는 데이터 타입을 사용.
- ... 키워드 사용
- stdarg.h에 정의되어 있다.
- : 가변인자. 변수가 몇개 들어올지 모를때 사용할 수 있다.
- va_start
- : va_list를 초기화
- va_list 변수 ap의 주소 값에다 고정파라미터(가변 파라미터 이외에 최소 1개의 고정 파라미터가 필요) 크기를 더한 위치로 ap를 초기화
- va_arg
- : va_list 포인터 변수가 위치한 부분의 데이터를 읽어 반환. 그 후 포인터를 증가 시킴.
- va_arg(변수, 데이터 타입);
- va_end
- : va_list 타입의 변수를 NULL로 초기화.
- va_list
- 어려웠던 점.
- va_arg를 사용하면, va_list의 변수가 자동으로 다음 주소로 변한다고 알고는 있었지만, 이 변수를 또다른 헬퍼 함수의 파라미터로 쓸때, 움직이질 않았다. 결과적으로, 이 변수의 주소, 즉, 주소의 주소를 넘겨주어야 움직였다.
- %i는 unsigned int로 받아서 출력을 해주면 되는데, 음수가 없다.
- Implementation.
- 프로그램의 일부 핵심 코드입니다.
/*
ft_putp.c
1. 본 함수 ft_putp()에서 0x를 먼저 출력 해준다.
2. printing() 함수에서 16진법에 맞게 주소를 출력해준다.
3. 다시 본함수 ft_putp()에서 출력해준 글자수를 세어주고 반환한다.
*/
static void printing(size_t n)
{
if (n < 16)
{
ft_putchar_fd("0123456789abcdef"[n % 16], 1);
}
else
{
printing(n / 16);
printing(n % 16);
}
}
int ft_putp(void *ptr)
{
size_t n;
int res;
res = 0;
n = (size_t)ptr;
res += ft_putstr_fd("0x", 1); //0x 먼저 출력해줌.
printing(n); //그 이외 부분 출력.
while (n != 0) //출력한 글자수 세어주고 반환.
{
n /= 16;
res++;
}
if (ptr == NULL) //0x0이 출력되기에 3이다. 그런데, 온라인 컴파일러의 경우 5가 나오는 경우도
res = 3; //존재한다. 따라서 작업 환경에 따라 주의를 요함.
return (res);
}
//receive address.
//print hexa address
/*
ft_printf.c
1. va_list 생성, 초기화.
2. check_str()에서 인자로 받은 문자열을 출력해주는데, 만약 %를 만나면, check_option()호출
3. check_option()에서는 옵션의 종류에 따라 적절한 함수를 실행 해준다.
4. 본 함수로 돌아와, va_list를 종료해주며, 출력한 글자수를 반환한다.
*/
#include "ft_printf.h"
static int check_option(va_list *ap, char c)
{
int res;
res = 0;
if (!(c == '%' || c == 'u' || c == 'p' || c == 'c' || c == 's' || \
c == 'd' || c == 'i' || c == 'x' || c == 'X'))
return (-1);
else if (c == '%')
res += ft_putchar_fd('%', 1);
else if (c == 'c')
res += ft_putchar_fd(va_arg(*ap, int), 1);
else if (c == 's')
res += ft_putstr_fd(va_arg(*ap, char *), 1);
else if (c == 'd' || c == 'i')
res += ft_putnbr_base_fd(va_arg(*ap, int), 1, c);
else if (c == 'u')
res += ft_putunbr_fd(va_arg(*ap, unsigned int), 1);
else if (c == 'x' || c == 'X')
res += ft_puthexnbr_fd(va_arg(*ap, long long), 1, c);
else if (c == 'p')
res += ft_putp(va_arg(*ap, void *));
return (res);
}
// 옵션을 체크하고, 그에 맞게 출력하는 함수를 불러오는 함수.
static int check_str(va_list *ap, char *format)
{
int res;
res = 0;
while (*format)
{
if (*format == '%')
{
format++;
if (*format == '\0')
break ;
if (*format == '%' || *format == 'p' || *format == 'u' \
|| *format == 'c' || *format == 's' || *format == 'd' \
|| *format == 'i' || *format == 'x' || *format == 'X')
res += check_option(ap, *format);
else
{
res += ft_putchar_fd('%', 1);
res += ft_putchar_fd(*format, 1);
}
}
else
res += ft_putchar_fd(*format, 1);
format++;
}
return (res);
}
int ft_printf(const char *format, ...)
{
int res;
va_list ap;
res = 0;
va_start(ap, format); //va_list의 변수 ap를 고정인자 format의 다음으로 위치시킨다. 즉, 초기화 작업.
res = check_str(&ap, (char *)format); //ap의 주소를 넣어줘야 함수간 이동하며 사용에도 지장이 없다.
va_end(ap); //가변인자 사용후 마무리.
return (res);
}
//va_list로 가변 인자를 받아 초기화후, check_str함수로 출력을 진행해준다.
/*
ft_puthexnbr_fd.c
1. check_nbr()에서 인자가 음수인 경우에 대해 처리를 해주며, 진법을 확정지어, 주소로 주어진 정수에 저장한다.
2. printing()에서 x,X 상황에 맞게 출력을 진행해준다.
3. 본 함수인 ft_puthexnbr()로 돌아와, 출력한 글자의 갯수를 세어주고 반환한다.
*/
#include "ft_printf.h"
static void printing(unsigned int num, char base)
{
if (num >= 16)
{
printing(num / 16, base);
printing(num % 16, base);
}
else
{
if (num >= 10 && base == 'X')
ft_putchar_fd('A' + num - 10, 1);
else if (num >= 10 && base == 'x')
ft_putchar_fd('a' + num - 10, 1);
else
ft_putchar_fd('0' + num, 1);
}
}
int ft_put_hex_nbr(unsigned int num, char base)
{
int res;
printing(num, base);
res = 0;
while (num != 0)
{
num /= 16;
res++;
}
if (res == 0)
res = 1;
return (res);
}
- 테스트

- 실행 결과
