libft 사용함수는 ft_strlen만을 사용했으며 함수에 맞게 libft.h와 Makefile을 수정했다.
해당 폴더에는 해더 파일이 들어있다.
#ifndef FT_PRINTF_H
# define FT_PRINTF_H
# include <stdarg.h>
# include <unistd.h>
# include "../libft/libft.h"
int ft_printf(const char *str, ...);
int ft_parsing(va_list args, const char format);
int ft_formats(va_list ap, const char *format);
char *base_type(const char type);
int ft_checkbase(va_list ap, const char format);
int ft_printi(int n, const char *base);
int ft_printu(unsigned int n, const char *base);
int ft_printp(unsigned long long n, const char *base);
void ft_printbase(unsigned long long num, const char *base);
int ft_printchar(int chr);
int ft_printstr(char *str);
#endif
해당 폴더에는 소스 파일들이 들어있다.
int ft_printf(const char *format, ...) {
va_list args;
int print_length;
va_start(args, format);
print_length = ft_formats(args, format);
va_end(args);
return (print_length);
}
해당 함수에서는 입력을 받고 결과를 return하는 기능으로 구현했다. va_list 가변 인자 포인터 변수를 args라는 이름으로 선언하고, va_start(args, format)을 이용해 가변 인자를 가리키게 했다.
int ft_parsing(va_list args, const char format) {
int print_length;
print_length = 0;
if (format == '%')
print_length += write(1, "%", 1);
else if (format == 'c')
print_length += ft_printchar(va_arg(args, int));
else if (format == 's')
print_length += ft_printstr(va_arg(args, char *));
else print_length += ft_checkbase(args, format);
return (print_length);
}
int ft_formats(va_list ap, const char *format) {
int i;
int print_length;
i = 0; print_length = 0;
while (format[i]) {
if (format[i] == '%') {
print_length += ft_parsing(ap, format[i + 1]); i++;
}
else
print_length += write(1, (format + i), 1); i++;
}
return (print_length);
}
이후 ft_fotmats에서 ft_parsing 함수를 이용해 문자열을 파싱하고 문자열의 수를 반환하도록 했다.
char *base_type(const char type)
{
if (type == 'u' || type == 'd' || type == 'i')
return ("0123456789");
else if (type == 'x' || type == 'p')
return ("0123456789abcdef");
else if (type == 'X')
return ("0123456789ABCDEF");
return (0);
}
int ft_checkbase(va_list ap, const char format)
{
int print_length;
char *base;
base = base_type(format);
print_length = 0;
if (format == 'd' || format == 'i')
print_length += ft_printi(va_arg(ap, int), base);
else if (format == 'u' || format == 'x' || format == 'X')
print_length += ft_printu(va_arg(ap, unsigned int), base);
else if (format == 'p')
print_length += ft_printp(va_arg(ap, unsigned long long), base);
return (print_length);
}
ft_checkbase에서 서식지정자에 맞는 베이스를 찾을 수 있도록 해주었다. 그 후 서식지정자에 맞게 d, i, u, x, X, p에 따라 출력할 수 있도록 ft_printbase를 해준다.
void ft_printbase(unsigned long long num, const char *base)
{
size_t len;
len = ft_strlen(base);
if (num >= len)
{
ft_printbase(num / len, base);
ft_printbase(num % len, base);
}
else
{
write(1, &base[num % len], 1);
}
}
int ft_printi(int n, const char *base)
{
int print_length;
print_length = 0;
if (n == -2147483648)
{
print_length += write(1, "-2147483648", 11);
return (print_length);
}
if (n < 0)
{
print_length += write(1, "-", 1);
n = -n;
}
if (n == 0)
print_length += write(1, "0", 1);
else
ft_printbase(n, base);
while (n)
{
print_length += 1;
n /= 10;
}
return (print_length);
}
int ft_printu(unsigned int n, const char *base)
{
size_t base_len;
int print_length;
print_length = 0;
base_len = ft_strlen(base);
if (n == 0)
print_length += write(1, "0", 1);
else
ft_printbase(n, base);
while (n)
{
print_length += 1;
n /= base_len;
}
return (print_length);
}
int ft_printp(unsigned long long n, const char *base)
{
int print_length;
print_length = 0;
print_length += write(1, "0x", 2);
if (n == 0)
print_length += write(1, "0", 1);
else
ft_printbase(n, base);
while (n)
{
print_length += 1;
n /= 16;
}
return (print_length);
}
write() 함수를 써서 출력하고 길이를 반환하는 기능을 한다.
int ft_printchar(int chr)
{
unsigned char c;
c = (unsigned char)chr;
write(1, &c, 1);
return (1);
}
int ft_printstr(char *str)
{
int print_length;
if (!str)
{
print_length = write(1, "(null)", 6);
return (print_length);
}
print_length = write(1, str, ft_strlen(str));
return (print_length);
}
서식지정자 c와 s의 출력을 처리하는 부분이다. 서식지정자 s의 경우, NULL값을 때 (null)이 출력되도록 하고 반환 값은 6으로 처리해주었다.
Makefile
Makefile은 '변수명 = 값'으로 변수를 선언 후 값을 저장해서 사용할 수 있다. 이후 변수를 사용할 때는 $(변수명)처럼 이용해서 사용한다.
"변수명 설명"
$a : 현재의 타겟명
$^ : 현재 타겟의 의존성
$? : 현재의 목표 파일보다 더 최근에 갱신된 파일 이름
$< : 현재의 목표 파일보다 더 최근에 갱신된 파일 이름
.PHONY: 실행 규칙을 위한 타겟명으로 사용하기 위한 것으로 타겟명으로
사용하는 단어가 파일명으로 있을 경우 충돌이 발생하는데,
이때 .PHONY에 타겟명을 명시하게되면 프로그램이 정상적으로 동작하게 된다.
@ : 명령어와 명령의 실행 결과를 출력하지 않는다.
-C dir : dir로 우선 이동할 수 있게된다. 순환 make에서 사용하게 된다.
NAME = libftprintf.a
CC = gcc
CFLAGS = -Wall -Wextra -Werror
AR = ar
ARFLAGS = rcs
RM = rm -rf
INCDIR = ./includes
LIBFT = ./libft
SRCS = ./srcs/ft_printf.c \
./srcs/ft_formats.c \
./srcs/ft_checkbase.c \
./srcs/ft_printbase.c \
./srcs/ft_printstr.c \
OBJS = $(SRCS:.c=.o)
%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@
$(NAME) : $(OBJS)
@make -C $(LIBFT)
@cp $(LIBFT)/libft.a $(NAME)
@ar rcs $@ $?
all : $(NAME)
clean :
$(RM) $(OBJS)
@make clean -C $(LIBFT)
fclean : clean
$(RM) $(NAME)
@make fclean -C $(LIBFT)
re: clean all
.PHONY: all clean fclean re