[42 Seoul] ft_printf

한종민·2023년 4월 25일
0

42SEOUL

목록 보기
4/6

ft_printf


프로그램 이름libftprintf.a
제출할 파일.c, /.c, .h, /.h, Makefile
Makefile 규칙all, clean, fclean, re, bonus
사용가능한외부 함수malloc, free, write, va_start, va_arg, va_copy, va_end
직접 만든 libft사용 가능
설명실제 printf의 동작을 모방한 ft_printf를 포함하는 라이브러리를 작성하세요.
  • ft_printf의 프로토타입은 int ft_printf(const char *, ...); 이어야 합니다.
  • 여러분은 libc의 printf 함수를 재구현해야 합니다.
  • 실제 printf처럼 버퍼 관리를 수행해서는 안 됩니다.
  • 다음 서식 지정자를 구현하세요 : cspdiuxX%

가변인자

함수에서 타입과 개수가 정해지지 않은 여러개의 인자를 받고 싶은 겨우 stdarg.h에 포함된 va_list 타입

va_arg, va_start, va_end함수를 활용한다.

1바이트 단위로 이동하기 위해서 va_list의 실제 타입은 char *로 사용되며, 이는 va_arg에서의 포인터 연산에 활용된다.

  • va_start → 가변 인자 리스트 포인터를 첫 주소로 초기화 시켜준다. va_start는 매크로 함수이고, va_list로 만들어진 포인터에게 가변 인자중 첫번째 인자의 주소를 가르쳐주는 매크로이다.
void va_start(va_list ap, var_name);

ap => va_list 로 만든 포인터가 담긴다.
var_name => 마지막 고정된 필수 인수가 담긴다.
  • va_arg → 자료현 타입 사이즈 만큼의 데이터를 반환하고, 포인터를 sizeiof 만큼 이동
#define va_arg(ap, t)					\
	 (((ap) = (ap) + __va_argsiz(t)),		\ // 먼저 ap 값을 밀어줌
	  *((t*) (void*) ((ap) - __va_argsiz(t)))) // 반환값으로 밀어주기 전의 주소에서 캐스팅 값
  • va_end → 리스트 포인터를 NULL로 가변인자 사용을 끝마침을 표시
    va_end는 매크로 함수이고, 사용했던 가변 인자 목록을 비워준다. 함수가 리턴하기 전에 반드시 호출해야 한다.

va_start와 va_end는 반환값이 없다.

stdarg.h에 포함된 함수와 타입의 정의는 다음과 같다.
매크로함수로 정의되어 있다

type va_arg(
   va_list arg_ptr,
   type
);
void va_copy(
   va_list dest,
   va_list src
); // (ISO C99 and later)
void va_end(
   va_list arg_ptr
);
void va_start(
   va_list arg_ptr,
   prev_param
); // (ANSI C89 and later)

가변인자의 메모리 구조

가변 인자들은 메모리 공간에 할당되어있다. 따라서 해당 가변인자를 활용하기 위해서 함수의 매개변수중 이전의 마지막 인자의 위치를 알아야한다.

write() 함수의 Return Value

성공하면 기록된 바이트 수가 반환된다.

오류가 발생되면 -1이 반환되며, enrno가 적정하게 설정된다.

https://linux.die.net/man/2/write

Testcase


#include <stdio.h>

int	main(void)
{
	int	print_return;
	int	ft_print_return;

	print_return = printf("%d", 123123123);
	printf("\n");
	ft_print_return = ft_printf("%%lklHElddddd");
	printf("\n");
	printf("original : %d   \n", print_return);
	printf("my printf : %d", ft_print_return);
}

My_code

#include "ft_printf.h"

static void	print_format(va_list ap, char c, int *i)
{
	if (c == 'c')
		ft_putchar(va_arg(ap, int), i);
	else if (c == 's')
		ft_putstr(va_arg(ap, char *), i);
	else if (c == 'p')
		ft_memory(va_arg(ap, void *), i);
	else if (c == 'd' || c == 'i')
		ft_putnbr_base(va_arg(ap, int), "0123456789", i);
	else if (c == 'u')
		ft_unsignednbr_base(va_arg(ap, unsigned int), "0123456789", i);
	else if (c == 'x')
		ft_unsignednbr_base(va_arg(ap, unsigned int), "0123456789abcdef", i);
	else if (c == 'X')
		ft_unsignednbr_base(va_arg(ap, unsigned int), "0123456789ABCDEF", i);
	else if (c == '%')
		ft_putchar(c, i);
	else
		*i = -1;
}

int	ft_printf(const char *format, ...)
{
	va_list	ap;
	int		i;

	va_start(ap, format);
	i = 0;
	while (*format)
	{
		if (*format == '%')
		{
			format++;
			print_format(ap, *(format), &i);
		}
		else
			ft_putchar(*(format), &i);
		if (i == -1)
		{
			va_end(ap);
			return (i);
		}
		format++;
	}
	va_end(ap);
	return (i);
}
  • 포맷으로 들어온 문자열과 가변인자들을 인수로 받는다.
  • va_start로 va_list의 ap를 format의 첫 주소를 가리키게한다.
  • 반복문을 통해 format의 주소를 증가시키며 format을 읽는다.
  • format이 %가 나왔을 때, print_format 함수로 들어가며, format의 주소와 반환해야할 i값의 주소를 들고 간다.
  • format이 서식지정자가 있는 문자일 경우, 각 문자열에 맞는 함수로 들어가게 된다. 서식 지정자가 아닌 경우 i의 값을 -1로 설정해주며 오류가 생겼음을 말해준다. 함수가 끝나고, i의 값이 -1일 경우 va_end를 통해, 가변 인자 리스트의 포인터를 null로 설정해주며, 끝났음을 표시.
  • 오류가 생기지 않았을 경우, ft_putchar 에서 한글자씩 write해줄때 i값을 하나씩 증가시켜준다.
  • ft_putstr에서 (char *)null이 들어왔을 때, “(null)”6글자를 예외적으로 write해주어야 하므로 이때 i를 6증가 시켜 주었다.
  • 마지막으로 format이 끝까지 돌았다면 가변 인자 리스트의 포인터를 null로 설정해주며, 끝났음을 표시하고 출력된 문자의 수 i를 리턴해준다.
profile
내가? 이것도?

0개의 댓글