일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 42
- so_long
- 42 so_long
- 지베르니
- 포르투갈 여행
- ft_printf
- 지베르니 계절 추천
- str함수
- gnl
- ecole42
- 알고리즘 기초
- push swap
- 42 pipex
- 지베르니 가을
- 이지젯
- pipex 42
- push swap 설명
- 에꼴42
- get_next_line
- 서울42
- 와인선별방법
- get next line
- 지베르니 여름
- 파리 피크닉
- libft
- 42 libft
- 굿노트 스티커
- pipex
- printf
- 와인 고르기
- Today
- Total
뇌 마음 반반저장소
[42_ft_printf] printf 만들기1 (feat. 자세한 va 함수 설명) 본문
일단 사용 가능한 함수를 확인해보면
malloc, free : 말록과 프리를을 사용해서 글을 쓸 공간을 할당하고 해제해 주고,
write : 이 함수를 사용해서 하나씩 출력한다?
va_start, va_arg, va_copy, va_end : 가변 인자로 구성된 함수를 작성한다..
자 그러면 가변 인자는 무엇일까?
가변 인자는 영어로 variable argument이다. variable은 변할 수 있는, 수식에 따라서 변하는 값을 의미하고 argument은 말 그대로 인수이다.
*흥미로운 점 : 인자(parameter)와 인수(argument)는 다르지 말입니다?
int ft_strlen(char *str) //인자 : 함수 원형에 들어가는 변수!
{
int i; //인수 : 함수 안에서 정의되고 사용되는 변수!
i = 0;
...
}
printf처럼 여러 가지 타입의 정보를 받아서 차례로 처리해 출력해주는 함수 속에 va 함수들이 들어있다.
이 함수들의 헤더는 <stdarg.h> 이다. 먼저 이 함수들을 어떻게 사용하는지 알아보자. 일단 이 함수들은 스택에 저장된다는 것을 잊지 말 것! 일단 ft_printf의 구조를 알고 넘어가면 아래에 더욱 쉽게 적용할 수 있다.
1. va_list
인수들의 리스트를 지정해 준다. 그냥 간단하게 이름만 정해주면 된다. 이 함수는 원래 구조체로 헤더에 심어져 있다! 아래의 구조체에 선언된 것처럼 char으로 1바이트씩 증감한다는 것을 의미한다.
//va_list의 본체!
typedef char *va_list;
//아래처럼 사용한다.
int ft_printf(const char *str, ...)
{
va_list prtlist;
//구조체 선언과 리스트 이름 등록!
...
}
2. va_start
여기 다음부터 라고 초기화시켜준다. 그 부분을 포인터로 가리킨다.
두 개의 인자가 들어가는데 하나는 시작되는 리스트의 이름(위치), 그리고 함수에 가장 처음으로 쓰인 인자의 이름이 들어간다. 한마디로 말하면 "우리 이제 시작할 건데~ 이 리스트에서 첫 번째 인자 먼저 넣어주고 그다음부터 시작한다~"라는 뜻이다.
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
//*중요* 여기서 INTSIZEOF(v)는 4바이트 즉 4의 배수로 사이즈를 맞춰서 저장하겠다는 뜻이다!
void va_start(va_list arg_ptr, prev_param); // (ANSI C89 and later)
//arg_ptr : 인수 목록의 포인터. 지금은 첫번째 자리를 가리키고 있을 것이다.
//prev_param : 다른 인수들 앞에 있어야 하는! 함수의 첫번째 매개변수.
//아래처럼 사용한다.
va_start(prtlist, str);
//리스트의 이름과, 첫번째 인자
우리는 str이 const char *str로 들어왔다. 그래서 1바이트씩을 차지하지만, 내장 함수의 define을 보면 실제로는 4바이트씩 공간을 잡아주겠다고 선언한 것과 같다. 그래서 char도 4바이트씩 할당이 된다. 이 함수는 이후 va_arg가 나오지 않으면 의미가 없다.
3. va_copy
이 함수는 strcpy함수처럼 src를 dest에 붙이는 함수이다!
void va_copy(va_list dest, va_list src); // (ISO C99 and later)
4. va_arg
이 친구가 이제 인수들을 때려 넣어주는 친구인데, 하나하나씩 넣기 위해 루프를 사용한다. va 함수의 반환 값은 바로 va_arg의 인수들이다.
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
예시) 만약에 double사이즈를 추가하려면 먼저 8바이트만큼 옮겨서 자리를 잡아주고 다시 돌아와서 값을 입력한다.
특이하게도 va_arg함수는 먼저 다음 포인터를 계산하고 다시 돌아와서 값을 입력하는 형태이다. 여기서 중요한 것이 지정된 타입을 절대적으로 알아야 하고, 들어온 가변 인자의 정확한 수를 알아야 한다. printf는 % d나 % s처럼 포맷으로 넘어오는 타입을 먼저 파악하고 그 인자의 자릿수를 착착 계산해 내보내기 때문이다. 바이트 수를 예상하고 임의로 공간을 정하는 것보다, 이렇게 하면 낭비하는 바이트 없이 모든 인수가 착착착 담기게 되겠군!
type va_arg(va_list arg_ptr, type);
//type : 인수의 유형
//아래처럼 사용한다.
int args; //타입은 원하는 저장 형태로!
int i;
i = 0;
args = 0;
while (i < str)
{
args += va_arg(prtlist, int); //계속 루프를 돌면서 args에 va_arg를 누적시킨다.
i++;
}
전체 문장 str에서 %가 나오면 변경시켜주는 if문으로 확장한다!
5. va_end
이 함수는 마지막에 리스트의 주소 값으로 가서 아래의 내장 함수 정의처럼 모두 0으로 초기화시켜준다!
#define va_end(ap) ( ap = (va_list)0 )
void va_end(va_list arg_ptr);
자 그러면 이 함수들을 참고해서 printf의 본체를 만들어 보자!
도움을 주고 싶으신 내용이나
틀린 내용이 있다면 댓글로 남겨주시고,
도움이 되었다면 공감 한 번씩 눌러주세요👍❤️
'42 > ft_printf' 카테고리의 다른 글
[42_ft_printf] printf 만들기2 (정적 변수와 자료형) (1) | 2022.12.18 |
---|---|
[42_ft_printf] 시작하며 (0) | 2022.12.16 |