[ft_printf](11)ft_print테스트
이번 포스트는 ft_print테스트에 관한 내용입니다.
1️⃣ 테스트진행(42TESTERS - PRINTF프로그램 이용)
- ft_printf과제의 테스터 프로그램이 여러가지가 있지만 그 중에서
42TESTERS-PRINTF
테스터 프로그램을 이용하여 테스트를 진행했습니다.
테스터 프로그램 링크 >> Mazoise - 42TESTERS-PRINTF
< 현재 나의 ft_printf 테스트 결과 >
- 틀린 케이스결과는
diff.txt
파일에서 자세히 확인할 수 있습니다.
< diff.txt >
2️⃣ 오류 케이스1
(1) %0. + (d, i, u, x, X, o)출력값 오류
%0.d, %0.d, %0.d, %0.d, %0.d" // 1st '*' = -4, 2nd '*' = 0
/* 나의 출력 */
8, -12, 123456789, 0, -12345678, 97, -2147483648, 2147483647 --- Return : 60
/* 올바른 출력 */
8, -12, 123456789, , -12345678, 97, -2147483648, 2147483647 --- Return : 59
printf("%0.d", 0);
의 케이스가 올바르지 않았습니다.%0.d
일 때 1, 2, ,3 ,4 …등은 그대로 출력되지만 0일 경우 공백을 출력해줘야 됩니다.- Ubuntu Vscode(wsl)에서는 위처럼
%0.d
처럼 서식자를 사용하는 것에 대해 오류를 출력해 주었기 때문에 따로 예외처리없이 코드를 구현했습니다. - 하지만 42Seoul의 과제는 MacOs운영체제를 기준으로 하는 과제이기 때문에 그 규칙을 따라야 합니다.
(2) 해결법
- 정수값의 숫자부분 문자열을 만들어주는 함수인
set_print_cpy
안에서ft_ullitoa_cpy
을 사용한 직후에 다음과 같은 예외처리 코드를 추가해줬습니다.
if (fg->point == 1 && fg->padding_back == 0 && fg->result[0] == '0' && fg->result[1] == '\0')
fg->result[0] = '\0';
3️⃣ 오류 케이스2
(1) 숫자 정밀도가 음수일 때(%.-n) 출력값 오류
"%.*i, %.*d, %.*d, %.*d, %.*d, %.*d, %.*d, %.*d" // 1st '*' = -4, 2nd '*' = 0
/* 나의 출력 */
0008, -0012, 123456789, 0000, -12345678, 0097, -2147483648, 2147483647 --- Return : 70
/* 올바른 출력 */
8, -12, 123456789, 0, -12345678, 97, -2147483648, 2147483647 --- Return : 60
.*
를 이용하여 음수를 받아올 때 양수부분의 값만큼 정밀도옵션을 적용해주어0
을 출력해준 것이 문제였습니다.- 역시 wsl환경에서는 적절한 사용의 방법이 아니라고 경고메시지를 출력해주었습니다.
하지만 역시나 macOs환경에 맞추어 예외처리코드를 구현해 주어야 합니다.
- 위의 경우처럼
.*
을 이용하는 것이 아닌.-2d
와 같이 직접적으로 음수값을 정밀도 옵션에 넣을 때는 macOs환경에서도 경고 메시지를 출력해 주었습니다.
(2) 해결법
- 기존에 코드를 짤 때 정밀도옵션에 음수가 들어오지 않는다는 가정으로 구현했습니다.
- 그래서 정밀도옵션을 읽어드리는 코드가 음수도 읽어드릴 수 있도록 바꿨습니다.
else if (fg->point == 1) // 포인트(.)뒤를 읽을 때
{
if (**format == '-')
fg->back_minus = 1; // back_minus은 새로운 구조체변수로 선언
else
fg->padding_back = 10 * fg->padding_back + (**format - '0');
}
/* 모든옵션을 읽어드린 후 */
if (fg->back_minus == 1) // back_minus가 1이라면
fg->padding_back *= -1; // 정밀도 저장값을 음수로 만듬
(3) 또다른 오류
"%.*i, %.*d, %.*d, %.*d, %.*d, %.*d, %.*d, %.*d" // 1st '*' = -4, 2nd '*' = 0
/* 나의 출력 */
8, -12, 123456789, , -12345678, 97, -2147483648, 2147483647 --- Return : 59
/* 올바른 출력 */
8, -12, 123456789, 0, -12345678, 97, -2147483648, 2147483647 --- Return : 60
- 37번테스트의 케이스 대부분이 통과하였습니다. 하지만
printf("%.*d", -4, 0);
의 케이스에서 오류가 났습니다. printf("%.*d", -4, 0);
는0
이 출력되야 합니다.- 규칙이라고 하기에는 약간 애매(?)한 부분이 있지만 이와 같이 출력이 되도록 다시 구현해야 됩니다.
/* 삭제할 코드 */
fg->padding_back = (fg->padding_back < 0) ? 0 : fg->padding_back; // 필요없는 코드
- 위의 코드는
fg->padding_back
이 음수일때0
으로 바꿔주는 코드입니다. - 하지만
fg->padding_back
이 음수일때0
은 표시되고 0일때 ``(공백)이 출력되게 해야되기 때문에 굳이 위의 코드가 필요없어졌습니다. - ‘0문자열을 공백으로 바꾸는 조건문’은 ‘케이스1’의 해결법으로 구현했던 코드에
fg->back_star == 0
조건을 추가한 코드입니다.
/* 정밀도옵션이 0일때도 처리해주는 조건문 */
if (fg->point == 1 && fg->padding_back == 0 && fg->result[0] == '0' && fg->result[1] == '\0')
fg->result[0] = '\0';
- 위의 코드는 기존
%0.d
를 처리하기 위해 만든 조건문이지만 알고리즘적으로%.0d
까지도 처리해줄 수 있는 조건문이 됬습니다.
4️⃣ 오류 케이스3
(1) 0플래그와 정밀도 옵션이 동시에 있을 때 출력값 오류
"%0*.*i, %0*.*d, %0*.*d, %0*.*d, %0*.*d, %0*.*d, %0*.*d, %0*.*d" // 1st '*' = 1, 2nd '*' = 0
/* 나의 출력 */
8, -12, 123456789, 0, -12345678, 97, -2147483648, 2147483647 --- Return : 60
/* 올바른 출력 */
8, -12, 123456789, , -12345678, 97, -2147483648, 2147483647 --- Return : 60
printf("%01.0d", 0);
의 출력에서 올바른 출력을 하지 못했습니다. (0
플래그와정밀도
옵션이 동시에 있는 경우)- 코드를 구현했을 때
0
플래그와.n
(정밀도 옵션)을 동시에 주면 컴파일 경고를 주었기 때문에 따로 처리를 하지않았습니다.
(2) 해결법
- 정수출력에서
0
플래그를 주면 남는 공백만큼 0을 붙여서 출력해줍니다. - 하지만 정밀도 옵션이 포함된다면
0
플래그를 무시해줘야하고 이러한 예외처리를 추가 해줬습니다.
< 기존 '0'플래그가 있을경우 처리코드 >
if (fg->zero == 1)
{
/* 코드 생략 */
}
else if (fg->zero == 0)
{
/* 코드 생략 */
}
< 수정된 '0'플래그가 있을경우 처리코드 >
if (fg->zero == 1 && fg->point == 0) //정밀도옵션이 없을 때 조건 추가
{
/* 코드 생략 */
}
else // else로 처리
{
/* 코드 생략 */
}
- 그런데 정밀도가 음수일 경우 정밀도 옵션은 무시되고
0
플래그 기능이 살아났습니다. fg->padding_back
변수는 음수일 경우 0이 되도록 바꿔줬기에fg->back_minus
변수를 이용하여 정밀도 옵션이 음수임을 판별했습니다.
< 정밀도가 음수일 경우를 예외처리한 조건문 >
if (fg->zero == 1 && (fg->point == 0 || fg->back_minus < 0)) // fg->back_minus변수를 이용하여 판단
{
/* 코드 생략 */
}
else // else로 처리
{
/* 코드 생략 */
}
5️⃣ 오류 케이스4
(1) 문자열서식자(s)의 최대너비옵션(.n)이 음수일 때 출력값 오류
"%.*s, %.*s, %.*s, %.*s, %.*s, %.*s, %.*s, %.*s" // 1st '*' = -4, 2nd '*' = 14
/* 나의 출력 */
, , , , , , , --- Return : 14
/* 올바른 출력 */
abcdefghijklmnop, -a, -12, 0, %%, -2147483648, 0x12345678, -0 --- Return : 61
%s
(문자열 서식자)일 때.n
(정밀도, 최대 출력길이)옵션이 음수일 경우 무시되도록 처리가 필요했습니다.
< 기존 문자열서식자의 정밀도 옵션처리 코드 >
if (fg->point == 1) // [. ]정밀도 옵션이 있을시
cnt = (cnt > fg->padding_back) ? fg->padding_back : cnt;
< 기존 문자열서식자의 정밀도 옵션처리 코드 >
if (fg->point == 1 && fg->padding_back >= 0) // fg->padding_back이 음수가 아닐 때 조건추가
cnt = (cnt > fg->padding_back) ? fg->padding_back : cnt;
6️⃣ 오류 케이스5
(1) `%p`(포인터 주소서식자) 출력값 오류
"%p, %x, %p, %x, %p, %x, 0%p</b>, %x" // 1st '*' = 5, 2nd '*' = 5
/* 나의 출력 */
0xc7e1ab0, c7e1ab0, 0xc5729c0, c5729c0, 0x1, 1, 00x0</b>, 0 --- Return : 54
/* 올바른 출력 */
0xc7e1ab0, c7e1ab0, 0xc5729c0, c5729c0, 0x1, 1, 0(nil)</b>, 0 --- Return : 56
printf("%p", NULL);
의 경우에 올바른 출력을 하지않았습니다.0x0
으로 출력되었지만 올바른 출력은(nil)
입니다.
(2) 해결법
< 수정 전 %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);
}
< 수정 후 %p서식자 출력코드 >
void print_p(va_list ap, t_flag *fg, const char c, int *len)
{
set_print_cpy(ap, fg, c);
/* (nil)을 출력할 경우 fg->padding_front에 -4해줍니다. */
fg->padding_front -= (fg->result[0] == '0' && fg->result[1] == '\0') ? 4 : 2;
if (fg->left != 1) // '-'왼쪽정렬이 아닐때
ft_print_word(' ', fg->padding_front, len);
if (fg->result[0] == '0' && fg->result[1] == '\0') // 주소값이 0일경우 조건문
ft_print_str("(nil)", len);
else
{
ft_print_str("0x", len);
ft_print_str(fg->result, len); // 서식지정자 출력 (메인 출력)
}
if(fg->left == 1) // 뒤쪽 공백출력 판단
ft_print_word(' ', fg->padding_front, len);
}
7️⃣ 오류 케이스6
(1) %s에 NULL이 들어올 때 출력값 오류
"%-2s, %.s, %-4s, %-2.4s, %-8.12s, %3s, %8s, %---2s, %.*s, %.0s, %.1s, %.2s, %.4s, %.8s" // 1st '*' = 12, 2nd '*' = 18
/* 나의 출력 */
Segmentation fault (core dumped)
/* 올바른 출력 */
(null), , (null), , (null) , (null), (null), (null), (null), , , , , (null) --- Return : 80
%s
(문자열 서식자)의 가변인자가NULL
이 들어오면cnt = ft_strlen(temp)
코드에서Segmentation fault (core dumped)
가 되었습니다.- 그렇기 때문에
ft_strlen
함수를 호출하기 전에NULL
에 대한 예외처리를 해주었습니다.
< 수정 후 %p서식자 출력코드 >
char* temp;
temp = va_arg(ap, char*);
cnt = ft_strlen(temp);
< 수정 후 코드 >
char* temp;
temp = va_arg(ap, char*);
if (temp == NULL)
temp = "(null)";
cnt = ft_strlen(temp);
(3) 또다른 오류
"%-2s, %.s, %-4s, %-2.4s, %-8.12s, %3s, %8s, %---2s, %.*s, %.0s, %.1s, %.2s, %.4s, %.8s" // 1st '*' = 12, 2nd '*' = 18
/* 나의 출력 */
< (null), , (null), (nul, (null) , (null), (null), (null), (null), , (, (n, (nul, (null) --- Return : 89
/* 올바른 출력 */
> (null), , (null), , (null) , (null), (null), (null), (null), , , , , (null) --- Return : 80
- NULL값이 들어올 때 정밀도가 6(
(null)
길이)보다 짧으면 출력되면 안됩니다. - 정밀도가 음수일 경우 정밀도옵션이 무시됩니다.
< 최종 구현된 %s(문자열)출력 코드 >
void print_s(va_list ap, t_flag *fg, int *len)
{
int cnt; // 실제 출력할 문자열길이
char* temp;
temp = va_arg(ap, char*);
if (temp == NULL)
{
temp = "(null)";
cnt = (fg->point == 1 && fg->padding_back < 6 && fg->padding_back > 0) ? 0 : 6;
}
else
cnt = ft_strlen(temp);
if (fg->point == 1 && fg->padding_back >= 0) // [. ]정밀도 옵션이 있을시
cnt = (cnt > fg->padding_back) ? fg->padding_back : cnt;
fg->padding_front -= cnt;
if (fg->left != 1) // 앞쪽너비
fg->padding_front -= ft_print_word(' ', fg->padding_front, len);
write(1, temp, cnt);
*len += cnt;
if (fg->left == 1) // 뒤쪽너비
fg->padding_front -= ft_print_word(' ', fg->padding_front, len);
}
42TESTERS 통과!!