왜 하필 atoi...?
피씬 때 부터 여러번 만들었던 atoi의 배신...
원본 함수와 실행값 비교
-9,223,372,036,854,775,808과 9,223,372,036,854,775,807기준으로 원본 함수는 값이 변하지 않는 것을 볼 수 있습니다. 도대체 저 수가 의미하는 바는 무엇이고 왜 이런 차이가 발생하는 걸까요?
1. long의 범위가 항상 같지 않다?!
구글에 long 범위를 찾아보자!
보통의 경우 long은 signed int와 같은 범위를 가집니다. 왜냐하면 둘 다 4bytes만큼 할당받기 때문이죠.
하지만, 사용하시는 환경에 따라 long에 할당되는 bytes가 달라질 수 있습니다.
1)32-bit
2)64-bit 윈도우 환경
3) 64-bit 유닉스 환경
맥은 유닉스 기반이기 때문에 long형으로 선언한 변수는 8bytes를 할당 받게됩니다.
long이 4bytes인 환경에서는 int와 범위가 같지만 우리가 지금 사용하는 맥 환경에서는 -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 가 되는 것이죠.
우리의 atoi가 원본 함수와 달라지는 값이 long 범위와 관련있다는 것을 알게되었습니다!
그런데 atoi는 분명히 int형을 반환하는 함수인데 이거랑 무슨 관련이 있는 걸까요?
2. 함수 코드 뜯어보기
1) atoi
#include <stdlib.h>
#undef atoi
/* Convert a string to an int. */
int
atoi (const char *nptr)
{
return (int) strtol (nptr, (char **) NULL, 10);
}
libc_hidden_def (atoi)
C
복사
허무하게도 atoi는 strtol이라는 함수의 반환값을 int로 형변환해서 보여주기만 합니다.
이유를 찾으려면 strtol 함수를 살펴볼 필요가 있겠네요!
2) strtol
해당 함수는 많은 주석들과 긴 코드 길이를 가지고 있어서 일부만 잠시 가져오도록 하겠습니다.
long strtol(const char *nptr, char **endptr, register int base)
C
복사
해당 함수의 원형을 보면 long형으로 반환하는 것을 알 수 있습니다. 즉, atoi 함수는 long형으로 반환된 값을 다시 int형으로 변환해서 반환해주는 것이죠.
그리고 코드의 중간 부분을 살펴보면 왜 long 범위를 넘었을 때 값이 고정되는지도 알 수 있습니다.
cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
cutlim = cutoff % (unsigned long)base;
cutoff /= (unsigned long)base;
for (acc = 0, any = 0;; c = *s++) {
if (ISDIGIT(c))
c -= '0';
else if (ISALPHA(c))
c -= ISUPPER(c) ? 'A' - 10 : 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
any = -1;
else {
any = 1;
acc *= base;
acc += c;
}
}
if (any < 0) {
acc = neg ? LONG_MIN : LONG_MAX;
errno = ERANGE;
} else if (neg)
acc = -acc;
if (endptr != 0)
*endptr = (char *) (any ? s - 1 : nptr);
return (acc);
C
복사
우리가 살펴봐야 할 부분을 강조표시해두었습니다.
먼저 해당 함수가 오버플로우를 어떻게 판단하는지 초반부를 통해 살펴볼 수 있습니다.
1.
입력받은 문자열의 수가 양수인지 음수인지를 판단하고 그에 맞는 최댓값 혹은 최솟값을 준비합니다.
2.
진법에 맞게 끝자리를 별도로 분리해둡니다. (cutlim)
3.
끝자리를 잘라냅니다. (cutoff)
4.
반복문이 돌아가면서 수를 만들다가(한 자리씩 늘어남!) 위에서 구한 cutoff보다 큰 데 아직 뒤에 붙일 자리 수가 남았거나 cutoff와 같은데 다음에 붙일 수가 cutlim보다 큰 경우 오버플로우로 판단합니다.
해당 과정을 거치고 내려오면 any가 -1로 정해진 상태라 오버플로우 상태에서의 반환값이 정해지게 됩니다.
살펴보니 음수일 때 LONG_MIN 양수일 때 LONG_MAX를 반환 값으로 정하고 에러코드를 설정하네요.
해당 코드를 통해서 왜 long 범위를 넘는 수가 들어왔을 때 원본 atoi 함수의 결과가 고정되는지 알 수 있었습니다.
*해당 내용은 strtol의 man을 통해서도 알 수 있습니다.