libc의 printf 함수를 다시 구현
Proto type. ▶ int ft_printf(const char *, ...);
External functs. ▶ malloc, free, write, va_start, va_arg, va_copy, va_end
처음 보는 va_* 관련 함수는 stdarg 헤더 파일에 정의된 함수들이다
▼▼▼ man 3 stdarg 를 번역 ▼▼▼
#include <stdarg.h>
void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_copy(va_list dest, va_list src);
void va_end(va_list ap);
호출 된 함수는 va_start (), va_arg (), va_copy () 및 va_end () 매크로에서 사용하는 va_list 유형의 객체를 선언해야합니다.
va_start () 매크로는 먼저 호출해야하며, 처리 할 각 인수에 대해 va_arg ()에 전달할 수있는 ap를 초기화합니다. va_end ()를 호출하면 더 이상 인수가 없다는 신호를 보내고 ap가 무효화됩니다. va_start ()에 대한 각 호출은 동일한 함수 내에서 va_end ()에 대한 호출과 일치해야합니다.
last 매개 변수는 변수 인수 목록 앞에있는 마지막 매개 변수의 이름입니다. 즉, 호출하는 함수가 유형을 알고있는 마지막 매개 변수입니다.
이 매개 변수의 주소는 va_start () 매크로에서 사용되기 때문에 레지스터 변수로 선언하거나 함수 또는 배열 유형으로 선언해서는 안됩니다.
va_arg () 매크로는 호출에서 다음 인수의 유형과 값이있는 표현식으로 확장됩니다. ap 매개 변수는 va_start ()에 의해 초기화 된 va_list ap입니다. va_arg ()에 대한 각 호출은 다음 호출이 다음 인수를 반환하도록 ap를 수정합니다. 매개 변수 유형은 지정된 유형을 가진 객체에 대한 포인터 유형을 유형에 *를 추가하는 것만으로 얻을 수 있도록 지정된 유형 이름입니다.
다음 인수가 없거나 유형이 실제 다음 인수의 유형과 호환되지 않는 경우 (기본 인수 승격에 따라 승격 됨) 무작위 오류가 발생합니다.
va_start () 매크로를 사용한 후 va_arg () 매크로를 처음 사용하면 마지막 인수가 반환됩니다. 연속 호출은 나머지 인수의 값을 반환합니다.
va_copy () 매크로는 이전에 va_start ()에 의해 초기화 된 변수 인수 목록 src의 상태를 va_end ()에 대한 중간 호출없이 va_start ()에 의해 이전에 초기화되지 않았어야하는 변수 인수 목록 dest에 복사합니다. ). dest에 보존 된 상태는 src에서 사용 된 것과 같은 방식으로 dest에서 va_start () 및 va_arg ()를 호출하는 것과 같습니다. 복사 된 변수 인수 목록은 이후에 va_arg ()로 전달 될 수 있으며 마지막으로이를 통해 va_end ()로 전달되어야합니다.
변수 인수 목록이 va_end ()에 의해 무효화되면 va_start ()로 다시 초기화하거나 va_copy ()를 사용하여 다른 변수 인수 목록의 사본을 만들 수 있습니다.
void foo(char *fmt, ...)
{
va_list ap, ap2;
int d;
char c, *s;
va_start(ap, fmt);
va_copy(ap2, ap);
while (*fmt)
switch(*fmt++) {
case 's': /* string */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c': /* char */
/* Note: char is promoted to int. */
c = va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
...
/* use ap2 to iterate over the arguments again */
/* ap2를 사용하여 인수를 다시 반복 */
...
va_end(ap2);
}
▼▼▼ 가변 인자 관련 참고 사이트 ▼▼▼
type va_arg(
va_list arg_ptr,
type
);
void va_copy(
va_list dest,
va_list src
);
void va_end(
va_list arg_ptr
);
void va_start(
va_list arg_ptr,
prev_param
);
/* arg_ptr
인수 목록에 대한 포인터입니다.
dest
Src 에서 초기화할 인수 목록에 대 한 포인터입니다.
src
대상 에 복사할 초기화 된 인수 목록에 대 한 포인터입니다.
prev_param
첫 번째 선택적 인수 앞에 오는 매개 변수입니다. */
va_list ap;
가변 인자를 저장하기 위한 구조체
va_start(ap, last);
va_start 는 함수에 전달 되는 인수 목록의 첫 번째 선택적 인수로 ap(arg_ptr) 를 설정 합니다. Arg_ptr 인수는 va_list 형식 이어야 합니다. 인수 last(prev_param) 은 인수 목록의 첫 번째 선택적 인수 바로 앞에 오는 필수 매개 변수의 이름if
va_arg(ap, type);
ap(arg_ptr) 에서 제공 하는 위치에서 형식의 값을 검색 하고, 형식의 크기를 사용 하여 다음 인수를 시작할 위치를 결정 하 여 목록의 다음 인수를 가리키도록 arg_ptr 를 늘립니다. 함수에서 여러 번 va_arg 사용 하 여 목록에서 인수를 검색할 수 있다.
예시 ) char *s = va_arg(ap, char *); int n = va_arg(ap, int);
va_copy(dest, src);
현재 상태의 인수 목록에 대 한 복사본을 만듭니다. Src 매개 변수는 이미 va_start 를 사용하여 초기화 해야 합니다. va_arg 호출로 업데이트 되었지만 va_end 를 사용 하 여 다시 설정 하지 않아야 합니다. Dest 에서 va_arg 에 의해 검색 되는 다음 인수는 src 에서 검색 된 다음 인수와 동일
va_end(ap);
모든 인수가 검색 된 후 va_end 는 포인터를 NULL 로 다시 설정 합니다. 함수가 반환 되기 전에 va_start 또는 va_copy 를 사용 하 여 초기화 된 각 인수 목록에서 va_end 를 호출
서식 지정자들을 구현 : cspdiuxX%
c | 문자 |
s | 문자열 |
p | 포인터의 메모리 주소 |
d | 부호있는 10진 정수 |
i | 부호있는 10진 정수 |
u | 부호없는 10진 정수 |
x | 부호없는 16진 정수(소문자) |
X | 부호없는 16진 정수(대문자) |
% | 형식 지정에 필요한 문자, % 출력하고 싶다면 %% 사용 |
서식문자에서 -0.* 플래그와 최소 필드 너비의 조합을 어떤 조합도 처리
%[플래그][폭][.정밀도][길이]서식지정자
각각의 옵션이 등장하는 순서가 존재하고 []로 둘러싸인 부분은 옵션이므로 생각가능
- | 왼쪽 정렬 |
0 | 출력하는 폭의 남는 공간에 0으로 채움 |
.숫자 | 지정한 숫자만큼 소수점 아래 자리 출력 |
* | 서식문자로 출력할 인수 앞에 가변인수로 필드의 폭을 결정 |
# flag '0' results in undefined behavior with 's' conversion specifier [-Wformat]
# precision used with 'p' conversion specifier, resulting in undefined behavior [-Wformat]
# flag '0' is ignored when flag '-' is present [-Wformat]
테스터기에만 맞추다보면 위와 같은 것도 구현을 해야되는 경우도 있다.
위에 대한 설명은 이후 나오는 코드를 풀어서 설명을 하고자 한다.
그리고 보너스 파트는 구현하지 않았기에 넘보지도 않았다.
- 문자를 출력하는 형식지정자
- 문자열을 출력하는 형식지정자
- 10진수 정수를 출력하는 형식지정자
- 0을 포함한 양의 정수를 출력하는 형식지정자 [10진수]
- 0을 포함한 양의 정수를 출력하는 형식지정자 [16진수]
- 주소를 출력하는 형식지정자
- 그리고 %
# 이미 완성을 한 직후에 gcc -Wall -Wextra -Werror으로 테스트 하면서 정리 중, 동작하지 않은 부분도 구현을 했음을 확인하고 읽기 바랍니다.
1. 문자를 출력하는 형식지정자
- - 플래그
- 0 플래그
- width
[c ], [00c], [ c] 와 같은 형식으로 출력한다.
width(폭) > 1 ▶▶▶ return (width)
width(폭) <= 1 ▶▶▶ return (1)
2. 문자열을 출력하는 형식지정자
- - 플래그
- width
- precision
25줄을 맞추기 위해 함수를 두가지로 만들었다.
- Null 인 문자열, precision 이 설정된 문자열
- 그 외의 문자열
Null 인 문자열의 경우, (null)이 출력이 되어야 한다. return 에서 출력된 것과 괄호를 다 포함하여 출력
precision 상황에 맞게 공백과 - 플래그에 따라 출력
그 외의 상황인 경우에도 위와 같이 width를 보고 공백과 -플래그에 따른다.
#6번 주소 형식지정자까지도 해결을 하였다.
3. 10진수 및 16진수 정수를 출력하는 형식지정자
여기서 부터는 3~5 번까지의 형식지정자를 묶어버렸다. 또한 숫자들은 문자열로 변환을 이미 시켜준 상황이기에 이후의 상황에서 문자열이라 지칭한다.
- width > precision && - 플래그 존재
- width > precision && - 플래그 없는
- 그 외의 상황들
1-1. precision 존재 하고, 숫자가 0 이라면 '\0' 으로 만들어서 처리
1-2. 음수라면, '-' 를 출력
1-3. 문자열 길이보다 precision 길이가 길다면 차이만큼 '0' 출력
1-4. 문자열 출력
1-5. width 보다 출력된 길이가 짧으면 폭만큼 ' ' 출력
칠판에 그림을 그려 1 번의 상황에서 발생되는 조건 3가지 정도가 나오는데, 그림으로 그려서 조건식을 만들면 편하다
return 값은 tmp를 두고 하나하나 출력하면서 값을 측정
제일 복잡했던 . . . (25줄 제한으로 인해)
2-1. precision 존재 하고, 숫자가 0 이라면 '\0' 으로 만들어서 처리
2-2. precision 보다 width 크다면, 차이만큼 ' ' 출력
2-3. 음수라면 '-' 를 출력
2-4. precision 이 문자열보다 길거나 precision이 존재하지 않고 0 플래그가 있으면, width와 문자열 길이의 차만큼 '0' 출력
2-5. 문자열 출력
return 값은 tmp를 두고 하나하나 출력하면서 값을 측정
3-1. precision 존재 하고, 숫자가 0 이라면 '\0' 으로 만들어서 처리
3-2. 음수라면 '-' 를 출력
3-3. 문자열보다 precision 크면 '0' 출력
3-4. 문자열 출력
precision과 문자열 비교시 음수를 제외한 길이를 비교한다.
6. 주소를 출력하는 형식지정자
itoa를 통해서 구분을 지을 때, 추가로 '0x' 붙여주는 작업을 하였고 2번과 동일한 구조로 진행하나
precision 존재하지만 0이고 주소값도 Null 이라면 '0x0' 이 아닌 '0x' 로 출력
7. 그리고 %
% 도 옵션이 존재하기에 출력할 할 때,
7-1. width 1보다 작으면 % 출력
7-2. -플래그 존재, 폭도 1보다 크면 % 출력 후 ' ' 출력
7-3. -플래그 존재하지 않고, 폭이 1보다 크면
7-3-1. 0플래그가 있다면 '0' 출력 후 % 출력
7-3-2. 아니라면 ' ' 출력 후 % 출력
http://elkha.kr/xe/study/197464
https://www.notion.so/ft_printf-1edb0fcfb7714a1da91432a4f23441ec
'42 Seoul' 카테고리의 다른 글
born2beroot (0) | 2021.06.22 |
---|---|
[42Seoul/GNL] get next line (0) | 2021.06.12 |
[42Seoul] libft (part . bonus) (0) | 2021.05.11 |
[42Seoul] libft (part. 2) (0) | 2021.05.09 |
[42Seoul] 함수 포인터 (0) | 2021.05.08 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!