1. 필수개념
printf 란?
•
처음 c언어를 배울때 출력방법중 하나로 알게되는 그 printf 맞다
•
일반 문자열에 %들을 통해서 문자열을 구성하여 출력한다.
int printf(const char *format, ...);
C++
복사
•
인자
◦
출력할 문자열
◦
%에 해당되는 변수들
•
반환값
◦
출력된 문자열 갯수
•
예시
int num = 5;
ret = printf("Num:%d\n", num);
C++
복사
◦
실제 출력 : Num:5$ ($는 엔터)
◦
% 뒤에 붙어있는 d 같은 애들을 서식지정자라고 부른다.
◦
이떄 ret 의 값은 6 이다. (엔터 포함)
•
서식지정자
◦
서식지정자에는 아래와 같은 규칙들이 존재한다.
%[flags][width][.precision][length]type
C++
복사
◦
아래 로직 부분에서 하나씩 알아보면서 직접 구현해본다.
◦
[ ] 는 옵션이고 % 와 type 은 필수로 들어가야한다.
가변인자
•
위의 프로토타입을 보면 두번째 인자가 ... 인것을 알수있다.
•
앞의 문자열에 있는 형식의 갯수에 따라서 인자의 갯수가 달라지기 때문에 가변인자를 쓴다.
•
2. 테스트
3. 로직
•
flag, width, precision 규칙은 이미 다른분들이 잘 정리했기 때문에 생략하고,
•
규칙에 맞게 잘 구현하는 방법을 적었습니다.
•
기본로직은 파싱 → 포장 → 출력 으로 이루어져 있습니다.
•
% 가 나올떄까지는 그대로 출력하고
•
% 가 나오기 시작한 부분부터 아래의 로직을 구현하면 됩니다.
•
3-1. 파싱
•
% 가 나오면 파싱을 시작합니다.
•
다음와같이 flag, width, precison, type 을 파싱해야합니다.
%[flags][width][.precision]type
C++
복사
•
파싱한 결과값을 저장할 구조체를 선언해줍니다.
typedef struct s_flag
{
int flag;
int width;
int precision;
int type;
} t_flag;
C++
복사
•
서식 지정자는 순서대로 들어옵니다.
◦
위의 형식을 지키지 않은 입력은 오류로 냅둡니다.
•
따라서 구현 해야 할 파트는 총 4개 입니다.
flag 파싱
•
- 또는 0 이 올 수 있습니다. 둘다 올 수 있습니다.
•
같은 문자가 여러개가 들어올수있습니다.
최종 값
width 파싱
•
0 으로 초기화해줍니다.
•
숫자가 오거나, * 이 올수있습니다.
◦
숫자 인 경우 atoi 하면 됩니다.
◦
* 인 경우 va_arg(ap, int) 로 값을 받아옵니다.
◦
이떄 값이 음수인경우
▪
flag 를 - 로,
▪
width 에는 절대값을 씌워 양수로 바꿉니다.
최종 값
precision 파싱
•
-1 로 초기화 해줍니다.
•
. 이 여러개 올 수 있습니다.
◦
0 으로 초기화 해줍니다.
◦
숫자가 오거나, * 이 올수있습니다.
▪
숫자 인 경우 atoi 하면 됩니다.
▪
* 인 경우 va_arg(ap, int) 로 값을 받아옵니다.
▪
이떄 값이 음수인경우
•
-1 로 초기화합니다.
최종 값
type 파싱
•
%cspdiuxX 중에 하나 올수있습니다.
•
만약에 없으면 오류이므로 다음 단계로 넘어가지 않습니다.
최종 값
주의! type 이 없는 경우 segfault 가 뜰수있습니다.
3-2. va_arg
•
va_arg 를 통하여 값을 읽습니다.
기본 보기
Search
•
long long 으로 캐스팅해서 변수로 저장하고 다니면 편합니다.
•
long long 이 8바이트 이기때문에 오버플로우되든 언더플로우되든 일단 저장해놓고
•
다시 값을 써야할때 해당 타입으로 캐스팅해서 쓰면 됩니다.
•
예)
char *str = "test";
long long value;
value = (long long)str;
printf("%s", (char *)value); //정상 출력
C++
복사
3-3. 포장
•
최종 출력 형식은 다음과 같습니다.
[left_margin][sign][prefix][zero][value][right_margin]
C++
복사
•
위의 형식을 저장하기 위한 구조체를 선언합니다.
typedef struct s_box
{
int left_margin; //왼쪽 스페이스 개수
char sign; // - 출력
int zero; //출력할 0의 개수
int prefix; //0x 출력 여부 (0 or 2)
long long value; //va_arg 값
int value_len; //va_arg 값의 길이
char base[20]; //진수
int base_len; //진수
int right_margin; //오른쪽 스페이스 개수
} t_box;
C++
복사
•
앞으로 content 를 다음과같이 정의하겠습니다.
content : [sign][prefix][zero][value]
최종 출력 : [left_margin][content][right_margin]
C++
복사
•
모든 변수를 0으로 초기화 합니다.
◦
ft_bzero(&content, sizeof(t_content));
•
먼저 value 에 위에서 va_arg 로 받은값을 대입합니다.
•
0 출력 로직(content)을 계산하고, 스페이스바 출력 로직(margin)을 계산하겠습니다.
•
type 에 따른 content 를 완성 시키도록 하겠습니다.
규칙 사전 지식 (참고)
type %, type c
기본 보기
Search
type s
type d, type i, type u
type x, type X, type p
margin 계산
•
content 가 완성되었기 때문에 margin 부분만 계산하면 된다.
•
먼저 content의 전체 길이를 구한다.
•
content_len = content→sign + ft_max(content→zero, 0) + content→prefix + content→value_len
•
flag 가 - 인 경우
◦
right_margin = ft_max(flag→width - content_len, 0)
•
아닌경우
◦
left_margin = ft_max(flag→width - content_len, 0)
•
최종 출력 길이
◦
ft_max(flag→width - content_len, 0) + content_len
3-4. 출력
•
이제 이미 다 만들어놓은 content 구조체를 가지고 출력만 잘 하면 된다 !!
[left_margin][sign][zero][prefix][value][right_margin]
C++
복사
•
위의 순서대로 출력하면된다.
1.
left_margin 만큼 스페이스 출력
2.
sign 이 1 이면 - 출력
3.
prefix 가 2 이면 0x 출력
4.
zero 만큼 0 출력
5.
value 출력
•
%, c
◦
value 값 출력
•
s
◦
value 가 있으면 value를 value_len 만큼 출력
◦
없으면 "(null)" 을 value_len 만큼 출력
•
d i u x X p
◦
value_len > 0 일떄만
▪
base 와 base_len 에 맞춰서 putnbr
6.
right_margin 만큼 스페이스 출력
4. 예외
•
타입 캐스팅에 유의하세요!!
5. 보너스
•
하는중....