
Born to Code
42서울 본과정
토글이 없어 보기 힘듭니다.
노션과 깃 페이지를 남겨두니 참고하세요.
보너스 안함
개발 전 기록사항
굳이 헤더를 여러개 만들 필요가 있을까? 우선 하나만 만들어보자.
파일 하나에 전부 구현하고, 파일을 나누어 보자.
버퍼관리를 하지 말라는 것은 문자열 관리 없이 바로 출력하라는 것일까?
%를 만나기 전까지는 출력하고 %를 만나면 처리하면 되려나?
저번에 멘토님이 말씀주신 LRparser에 대해 알아보자 (안함)
반환값은 출력한 문자열의 길이, \n포함, 에러시 아무것도 출력안하고 0반환
플래그 중복 가능 여러개 중복 가능
width든 뭐든 %와 타입 사이에 플래그는 옮
width에서 int범위를 초과하면 오버플로우가 아니라 그냥 출력이안됨
기능구현목록
출력할 문자열 (string)과 데이터의 문자열 (data_str)은 서로 다름.
%를 만나지 않는다면 계속 출력.% 를 만났다면, flag, width, precision 순으로 파싱.type 을 만났다면, 해당 출력 함수 호출.FLAG_STAR)가 세워져 있다면, 가변인자에서 문자를 받음.FLAG_STAR)가 세워져 있지 않고 포인터의 문자가 digit 이라면, 숫자 파싱.width 가 세워져 있지 않다면 0..을 가리키고 있다면, 플래그프리시전 (FLAG_PREC)을 세우고, precision을 0으로 초기화. (.만 나오고 숫자가 안나올 수 있기 때문)*을 가리키고 있다면, 가변인자에서 숫자를 받아와서 저장, 플래스스타프리시전 (FLAG_STAR_PREC)을 세움.*이 아니면서 숫자라면, 숫자 파싱.-1.- , 0 , * 이 그만 나올때까지 포인터 이동.flag 에 비트로 저장.width값을 비교하여 1 or width 값만큼 출력길이 지정.0 혹은 ' ' 값 저장.0 추가.- 추가.null일 경우 문자열을 널로 채움.width를 비교하여 출력할 문자열 할당.0 혹은 ' ' 값 저장.-로 바꾸고 가변인자 문자열의 -를 0으로 바꿈.)print_c와 같으나, 가변인자를 받지 않고 %를 넣음.null일 경우 2칸 할당 뒤 숫자 0, 아닐 경우 16칸을 할당하고 데이터를 16진수로 변환하여 넣음.+2 만큼 저장.width의 길이와 출력문자열의 길이를 비교하여 문자열 할당.0 혹은 ' ' 값 저장.0과 x를 넣음.null일 경우 (null) 문자열, 아닐경우 그대로 저장.width값과 문자열의 길이를 비교하여 출력문자열의 길이 할당.print_int 와 같으나 마이너스 부분만 삭제.print_p와 같으나 0x 부분만 삭제. 대문자소문자는 put_number_base에서 처리피신때부터 악명높았던 printf가 왜 그만한 평을 받고 있는지 잘 알게 되었다.
각 타입의 출력형식 뿐 아니라 플래그의 적용이나 옵션의 유무도 달랐다.
% 를 만나기 전까진 한글자씩 출력.% 를 만나면, 플래그 길이 정밀도 순으로 파싱 후 타입 확인.% 부터 플래그, 길이, 정밀도 순으로 파싱을 하고 각 값을 저장한다.# define FLAG_MINUS 1
# define FLAG_ZERO 2
# define FLAG_STAR 4
# define FLAG_STAR_PREC 8
# define FLAG_PREC 16
/*
typedef struct s_options
{
unsigned char flag;
int width;
int precision;
char type;
} t_options;
*/
unsigned char flag;
flag = 0; // 0000 0000
while (**p == '-' || **p == '0' || **p == '*')
{
if (**p == '-')
flag |= FLAG_MINUS; // => flag = flag | FLAG_MINUS
else if (**p == '0')
flag |= FLAG_ZERO; // 0000 0010
else if (**p == '*')
flag |= FLAG_STAR; // 0000 0100
(*p)++;
}
/*
* `|` 는 OR 연산으로 flag | FLAG_MINUS 의 값은 0000 0001이 됨.
*/
if (op.flag & FLAG_ZERO)
string[i++] = '0';
/*
* '&' 는 AND연산으로 flag & FLAG_ZERO 의 반환값은 FLAG_ZERO, 즉 양수임
* 만약 flag가 0000 0000 이라면(오른쪽 두번째 숫자가 0일 경우), 반환값은 0임.
*/

단 x(적용되지 않음)의 경우, undefinded behavior이지 결과값은 플래그가 적용된 상태로 나옴.(클러스터 맥 기준)
플래그가 두개 동시에 나오는 경우, 0 플래그는 제외하고 -플래그만 사용한다.
또한, 플래그가 아래 예시처럼 여러개가 나올 경우에도 작동된다.


물론 위 경우처럼 - 과 0 이 동시에 나올 경우, undefined behavior이다.
하나만 여러개 나올 경우 경고조차 발생하지 않는다.
플래그는 어느 위치에서나 적용될 수 있지만, 경고 메세지가 뜬다.
(%--10.3--s 의 경우, invalid conversion specifier '-')
따라서, 이번에 작성한 ft_printf 코드는 undefinded behavior에 관하여 출력내용을 따라하기도, 에러로 판단하고 출력하지 않기도 한다.
while (**p == '-' || **p == '0' || **p == '*')
{
if (**p == '-')
flag |= FLAG_MINUS;
else if (**p == '0')
flag |= FLAG_ZERO;
else if (**p == '*')
flag |= FLAG_STAR;
(*p)++;
}
% 에서부터 type 까지의 부분에 대하여 출력될 총 문자열의 길이를 뜻한다.데이터의 길이(가변인자로 받았고, 가공되어 출력 전의 상태의 데이터)보다 값이 크다면 width만큼, 아닐 경우 데이터의 길이만큼 출력된다.*이 나오면 가변인자에서, 나오지 않았다면 숫자가 있을 경우 파싱하여 값을 저장한다.*과 숫자가 같이 올 수 없다.- 플래그 + 숫자로 간주한다.if (*flag & FLAG_STAR)
{
width = va_arg(ap, int);
if (width < 0)
{
*flag |= FLAG_MINUS;
width *= -1;
}
}
else if (ft_isdigit(**p))
width = ft_printf_atoi(p);
.을 사용.*을 이용하여 가변인자에서 불러올 수 있다.. 뒤에 숫자 없이 바로 type 이 올 수 있다. (이때, 0으로 간주 ex)%.d)if (**p == '.')
{
(*p)++;
*flag |= FLAG_PREC;
precision = 0;
if (**p == '*')
{
(*p)++;
precision = va_arg(ap, int);
*flag |= FLAG_STAR_PREC;
}
else if (ft_isdigit(**p))
precision = ft_printf_atoi(p);
}
코드를 넣으면 글이 너무 길어져 삭제하였습니다.
코드가 필요하신 분은 노션을 참고해주세요.
구현방법은 정말 여러가지입니다. 참고용으로만 보시길 바랍니다.
데이터 문자열과 출력문자열은 서로 다릅니다.
가변인자란?
...을 사용하여 가변인자임을 알린다.int ft_printf(const char *str, ...)헤더 : <stdarg.h>
사용 :
va_list 변수를 만든다.
해당 변수는 각 가변인자의 시작 주소를 가리키는 포인터이다.
va_start 함수를 이용하여,
해당 함수는 가변인자를 가져올 수 있도록 포인터를 설정(초기화)하는 함수이다.
- va_start(ap, str)에서 ap는 가변인자(va_list) 변수, 그 뒤 고정인자를 넣는다.
이때 가변인자는 고정인자의 뒤에 위치하게 된다.
int ft_printf(const char *str, ...)
{
va_list ap;
int count;
va_start(ap, str);
count = myprintf(str, ap);
va_end(ap);
return (count);
}
va_arg함수를 이용하여 가변인자를 가져온다.
va_arg(va_list, 자료형)은 va_list가 가리키는 값을 자료형의 크기만큼 리턴한 후, va_list를 자료형 크기만큼 뒤로 옮긴다.
![]()
va_end 함수를 사용하여 va_list를 초기화한다.
<출처>
출력된 길이를 반환한다.
오류 발생 시 -1을 리턴한다.
코드 삭제함.
data의 크기를 int로 받아야 한다.
이에 관련한 설명은 아래 링크로 대신한다.
char type in va_arg stack_overflow
width의 길이가 1보다 길면, 출력할 길이를 width만큼 확보한다.0플래그가 적용될 사항일 경우 0, 아닐 경우 빈칸을, 확보한 string에 넣는다.-플래그 유무에 따라 문자를 앞 혹은 뒤에 넣고 출력한다.코드 삭제함.
가변인자를 char * 자료형으로 받아온다.
저장되는 데이터는 문자열의 주소값이다.
data가 null(0)일 경우 문자열은 (null)이 된다. 이때, 괄호가 포함된다.
정밀도가 음수이거나 문자열의 길이보다 길 경우 문자열을 그대로 두고, 양수이면서 문자열의 길이보다 짧을 경우 문자열을 정밀도의 길이만큼만 자른다.
if (op.precision < 0 || op.precision > ft_strlen(data))
pro_data = ft_strndup(data, ft_strlen(data));
else
pro_data = ft_strndup(data, op.precision);
width이 현재 가공된 데이터(pro_data)보다 길 경우 width만큼, 짧을 경우 가공된 데이터 길이만큼 확보한다.0플래그가 적용될 사항일 경우 0, 아닐 경우 빈칸을, 확보한 string에 넣는다.-플래그 유무에 따라 문자열를 앞 혹은 뒤에 넣고 출력한다.코드 삭제함.
data를 int형으로 받아온다.
data를 itoa함수를 사용하여 문자열로 바꾼다. 이때 음수는 고려하지 않는다.
(문자열에 '-' 안넣음)
정밀도가 문자열의 길이보다 길다면 그 차만큼 앞에 0을 추가한다.
data가 음수면 앞에 -를 추가한다.
정밀도가 0이면서 data가 null이면 문자열을 널값이 들어있는 1칸짜리로 바꾼다.
width값이 데이터 문자열의 길이보다 길다면 width, 아니라면 데이터문자열 길이만큼 출력할 문자열의 길이를 확보한다.
0플래그가 적용될 사항일 경우 0, 아닐 경우 빈칸을, 확보한 string에 넣는다.
-플래그 유무에 따라 문자열를 앞 혹은 뒤에 넣는다.
-플래그가 적용되지 않으면서 때 데이터가 음수라면, 출력할 문자열의 맨 앞을 -로 바꾸고 중간에 들어있는 -를 0으로 치환한다.
출력
코드 삭제함.
integer 출력방법에서 -와 관련된 부분을 삭제하고 출력.
주소값 출력이지만 가변인자로 일반변수를 넣으면, 값을 16진수로 변환하여 출력한다.
코드 삭제함.
data를 void *형으로 받아오고 unsigned long으로 캐스팅한다.
data가 null이라면 1칸, 아닐 경우 16칸의 데이터 문자열의 길이를 확보한다.
data가 null이라면 문자 0, 아니라면 put_number_base 함수로 16진수로 변환한 값을 데이터 문자열에 저장한다.
data가 null이면서 정밀도가 0인 경우 문자열을 널값이 들어간 1칸짜리로 바꾼다.
정밀도가 문자열의 길이보다 길다면 그 차만큼 앞에 0을 추가한다.
문자열의 길이 + 2만큼 출력할 문자열의 길이를 확보한다.
문자열의 길이 + 2보다 width길이가 길다면 그만큼 더 확보한다.
0플래그가 적용될 사항일 경우 0, 아닐 경우 빈칸을, 확보한 string에 넣는다.
-플래그 유무에 따라 문자열를 앞 혹은 뒤에 넣는다.
이때, -플래그의 적용을 받는다면 출력할 문자열의 앞 2칸을 확보한다. (2칸 뒤에 데이터를 넣는다.)
데이터문자열이 들어간 위치 앞 2칸을 0x로 채운다.
출력
코드 삭제함.
%p에서 출력할 문자열 2칸 더 확보, 0x 추가 부분을 제외하고 작성한다.
코드 삭제함.
%c에서 가변인자를 받아오는 부분과 문자를 출력할 문자열에 삽입하는 과정을 %로 바꾸면 된다.
jaeseokim님의 블로그와 깃을 참고하여 작성하였다.
warning
리눅스 토르발즈의 printf를 참고하는 도중, 실제 printf에서 가변인자의 개수나 경고를 발생시키는 주체 등에 관한 궁금증이 생겼다.
결론은, printf에서 경고 등은 컴파일에서 발생시키는 것으로 현재 나의 실력으로는 구현을 하거나 더 알아보기 힘들다고 판단했다.
가변인자의 끝
실제 printf에서 가변인자의 개수를 덜 주거나 더 주는 경우에 관하여, 가변인자의 개수를 파악하거나 알 수 있는 방안이 있는지 궁금했다.
결론은, 사실상 불가능 한 것으로 봐야할 것이다.
기타 참고