[ft_printf](8)서식지정자(x, X, o, p)출력, 코드 중간 정리
이번 포스트는 (ft_print)서식지정자(x, X, o)출력, 코드 중간 정리에 관한 내용입니다.
1️⃣ 부호없는 16진수, 8진수 서식지정자(x, X, o) 규칙
(1) %x 규칙[%x, %X, %o은 규칙이 비슷]
printf("% 7.6x끝\n", 1234); //컴파일 오류
printf("%+8.6x끝\n", 1234); //컴파일 오류
printf("%8.6x끝\n", 1234);
printf("%#*.*x끝\n", 8, 3, 12324);
printf("%-8.6x끝\n", 1234);
printf("%8.3x끝\n", 1234);
printf("%3.3x끝\n", 1234);
printf("%x끝\n", 1234);
printf("------------------\n");
printf("%8x끝\n", 1234);
printf("%-8x끝\n", 1234);
printf("%08x끝\n", 1234);
printf("%#x끝\n", 1234);
printf("%-#8x끝\n", 1234);
printf("%#8x끝\n", 1234);
printf("------------------\n");
printf("%#08x끝\n", 1234);
printf("%lx끝\n", (unsigned long int)123456);
printf("%llx끝\n", (unsigned long long int)123456);
printf("%hhx끝\n", 123456);
printf("%hx끝\n", 123456);
0004d2끝
0x3024끝
0004d2 끝
4d2끝
4d2끝
4d2끝
------------------
4d2끝
4d2 끝
000004d2끝
0x4d2끝
0x4d2 끝
0x4d2끝
------------------
0x0004d2끝
1e240끝
1e240끝
40끝
e240끝
- ' '플래그(공백)와 ' + '플래그 사용이 불가능 합니다.
- '* . *'옵션 사용이 가능합니다.
- ' # '플래그사용시 %x, %는 각각
0x
,0X
를 %o는0
을 붙여서 출력해주었습니다. 단, ' 0 '플래그에 의해0
으로 공백이 채워진경우0
보다 우선순위로 출력됩니다. - 나머지 규칙은 %d, %i, %u 때와 동일 합니다.
(2) fg->hash요소 삭제
- 위에서 규칙을 알아본 결과 서식지정자(%x, %X, %o)는 ' + '플래그, ' '플래그를 사용하지 못하며 부호없는 정수이기에 ' - '(음수)기호가 올 일이 없습니다. 또한 ' # '플래그, ' '플래그, ' + '플래그의 출력 규칙은 같습니다.
- 그렇기 때문에 ' # '플래그는
fg->plus
를 사용해도 중복없이 사용이 가능합니다. - 또한 코드도 깔끔해 질 것입니다.
/*-----변경 전-----*/
if (**format == '#')
fg->hash = 1;
/*-----변경 후-----*/
if (**format == '#')
fg->plus = '0';
- ' # '플래그일 때
fg->plus
값에'0'
을 지정하여 %o(8진수)일 때0
을 붙여주도록 했습니다. - %x, %X(16진수)일 때는
fg->plus == '0'
의 조건문을 이용하여0x
혹은0X
를 붙여줄지의 여부를 판단하도록 했습니다.
2️⃣ malloc(동적메모리할당)함수 없애기
참고포스트 > [C]동적메모리 할당 함수
- 기존의
ft_ullitoa_malloc
함수에서malloc
함수를 이용하여 동적할당을 해주었습니다. - 하지만
malloc
(동적할당)을 사용하는데 단점이 많으며 그만큼 위험하기도 합니다. - 실제로
ft_ullitoa_malloc
함수는 정수를 단순히 문자로 만들어 메모리에 저장하는 함수로 많은 메모리를 차지하지 않습니다. 그렇기 때문에 미리 배열을 선언해서 복사하는 것이 더 나을 것 같습니다. - 하지만
ft_ullitoa_malloc
함수 내부에서 배열을 선언하여 반환해주면 댕글링 포인터(danglinh porinter)가 일어날 수 있기 때문에 매개변수로 배열을 받아 복사하는식으로 다시 구현할 예정입니다.
(1) 최대 배열의 크기 구하기
- ft_print에 사용될 최대 크기의 자료형은
unsigned long long int
일 것이며 8진수(%llo)형태로 출력했을 때 가장 길게 출력될 것입니다.
< 가장 긴 길이의 정수 출력 >
#include <stdio.h>
#include <limits.h>
int main(void)
{
unsigned long long int num;
int cnt;
num = ULLONG_MAX;
printf("%llo%n\n", num, &cnt);
printf("길이: %d\n", cnt);
}
1777777777777777777777
길이: 22
- 배열의 크기를
'\0'
의 자리까지 고려해서 23으로 잡으면 충분할 것 같습니다.
(2) ft_ullitoa_malloc함수 수정하기
< 수정 전 ft_ullitoa_malloc함수 >
char *ft_ullitoa_malloc(unsigned long long int num, char *num_version)
{
size_t num_len;
int cnt;
unsigned long long int nb;
char *result;
num_len = ft_strlen(num_version);
nb = num;
cnt = 1;
while ((nb /= num_len) > 0)
cnt++;
if(!(result = (char*)malloc(sizeof(char) * (cnt + 1))))
return (NULL);
result [cnt] = '\0';
while (cnt > 0)
{
result[cnt - 1] = num_version[num % num_len];
cnt--;
num /= num_len;
}
return (result);
}
< 수정 후 ft_ullitoa_cp함수 >
void *ft_ullitoa_cpy(unsigned long long int num, char *num_version, char* str)
{
size_t num_len;
int cnt;
unsigned long long int nb;
num_len = ft_strlen(num_version);
nb = num;
cnt = 1;
while ((nb /= num_len) > 0)
cnt++;
str[cnt] = '\0';
while (cnt > 0)
{
str[cnt - 1] = num_version[num % num_len];
cnt--;
num /= num_len;
}
}
3️⃣ char형, short형 받는 방식 변경
- 기존에
t_flag
구조체에char
,short
,unsigned char
,unsigned short
형의 요소를 추가하여va_arg(ap, int)
를 강제로 받아오고자 했습니다. - 하지만 이런식으로 작은 데이터 자료형에 큰 데이터 자료형의 값을 복사하려고 하다보니 예기치 못한 결과값이 출력되었습니다.
- 반대로 큰 데이터 자료형에 작은 데이터 자료형의 값을 복사하는 것은 정상적으로 출력 되었습니다.
- 그래서
va_arg(ap, int)
를 강제 형변환을 하여fg->lli
에 직접 넣어주는 식으로 바꾸어 주었습니다. - 기존의
t_flag
구조체의short
,unsigned char
,unsigned short
형 요소들은 없앴습니다. (char
형은 문자(c)출력을 위해 필요)
< 수정 전 길이 조정 조건문 >
if (fg->data_length == -2)
{
fg->c = va_arg(ap, int);
fg->lli = fg->c;
}
else if (fg->data_length == -1)
{
fg->s = va_arg(ap, int);
fg->lli = fg->s;
}
else if (fg->data_length == 0)
fg->lli = va_arg(ap, int);
else if (fg->data_length == 1)
fg->lli = va_arg(ap, long int);
else if (fg->data_length == 2)
fg->lli = va_arg(ap, long long int);
if (fg->lli < 0)
{
fg->ulli = fg->lli * (-1);
fg->plus = '-';
}
else
fg->ulli = fg->lli;
< 수정 후 길이 조정 조건문 >
if (fg->data_length == -2)
fg->lli = (char)va_arg(ap, int);
else if (fg->data_length == -1)
fg->lli = (short)va_arg(ap, int);
else if (fg->data_length == 0)
fg->lli = va_arg(ap, int);
else if (fg->data_length == 1)
fg->lli = va_arg(ap, long int);
else if (fg->data_length == 2)
fg->lli = va_arg(ap, long long int);
if (fg->lli < 0)
{
fg->ulli = fg->lli * (-1);
fg->plus = '-';
}
else
fg->ulli = fg->lli;
4️⃣ 16진수 서식지정자(x, X)출력함수 구현
< print_xX함수 >
void print_xX(va_list ap, t_flag *fg, const char c, int *len)
{
set_print_cpy(ap, fg, c);
if (fg->plus != 0)
fg->padding_front -= 2;
if (fg->left != 1) // '-'왼쪽정렬이 아닐때
{
if (fg->zero == 1)
{
if (fg->plus != 0)
(c == 'x') ? ft_print_str("0x", len) : ft_print_str("0X", len);
fg->padding_front -= ft_print_word('0', fg->padding_front, len);
}
else if (fg->zero == 0)
{
fg->padding_front -= ft_print_word(' ', fg->padding_front - fg->padding_back, len);
if (fg->plus != 0)
(c == 'x') ? ft_print_str("0x", len) : ft_print_str("0X", len);
fg->padding_back -= ft_print_word('0', fg->padding_back, len);
}
}
else // '-'왼쪽정렬일 때
{
if (fg->plus != 0)
(c == 'x') ? ft_print_str("0x", len) : ft_print_str("0X", len);
fg->padding_front -= fg->padding_back;
fg->padding_back -= ft_print_word('0', fg->padding_back, len);
}
ft_print_str(fg->result, len); // 서식지정자 출력 (메인 출력)
if(fg->left == 1) // 뒤쪽 공백출력 판단
ft_print_word(' ', fg->padding_front, len);
}
5️⃣️ 포인터 주소 서식지정자(p) 규칙
< %p(포인터 주소) >
printf("%p끝\n", temp);
printf("%17p끝\n", temp);
printf("%-17p끝\n", temp);
printf("%+p끝\n", temp); // 컴파일 오류
printf("%8.6p끝\n", temp); // 컴파일 오류
printf("%#*.*p끝\n", 8, 3, temp); // 컴파일 오류
printf("%-8.6p끝\n", temp); // 컴파일 오류
printf("%8.3p끝\n", temp); // 컴파일 오류
printf("%3.3p끝\n", temp); // 컴파일 오류
printf("%08p끝\n", temp); // 컴파일 오류
printf("%#p끝\n", temp); // 컴파일 오류
printf("%-#8p끝\n", temp); // 컴파일 오류
printf("%#8p끝\n", temp); // 컴파일 오류
printf("%#08p끝\n", temp); // 컴파일 오류
0x7fffd986e715끝
0x7fffd986e715끝
0x7fffd986e715 끝
- 주소포인터 서식지정자(%p)는 ' - '플래그(왼쪽여백)와 너비옵션만 사용이 가능합니다.
- 그리고 출력값은 16진수로 출력되며 출력값 앞에
0x
를 붙여주는 것만 고려해주면 됩니다.
6️⃣ 포인터주소 서식지정자(p)출력함수 구현
< print_p함수 >
void print_p(va_list ap, t_flag *fg, const char c, int *len)
{
set_print_cpy(ap, fg, c);
fg->padding_front -= 2;
if (fg->left != 1) // '-'왼쪽정렬이 아닐때
ft_print_word(' ', fg->padding_front, len);
ft_print_str("0x", len);
ft_print_str(fg->result, len); // 서식지정자 출력 (메인 출력)
if(fg->left == 1) // 뒤쪽 공백출력 판단
ft_print_word(' ', fg->padding_front, len);
}