언뜻보기에 atoi
은 strtol
또는 'string to long'(int와 long은 모두 내 플랫폼에서 32 비트이고 'long long'은 더 큰 것을 얻는 데 필요합니다.). 컴파일러는이를 직접 호출에 최적화 할 것이지만 사이클을 절약 할 수 있습니다. 표면 상 속도에 민감한 애플리케이션의 경우 strtol()을 바로 호출하면됩니다. 또는 오히려 strtoul
으로 'string to unsigned long'으로 전화하십시오. 주목할만한 다른 것을 호출하지 않는 함수가 생겼으므로 이제 살펴 보겠습니다. 지금은 재진입 정보를 무시하십시오. 괄호로 조심하고, 일부 ifs 그들과 관련된 elses 부족 (이는 가난한 스타일의 IMO, 나는 괄호를 사방에 선호).
unsigned long _strtoul_r
(struct _reent *rptr, _CONST char *nptr, char **endptr, int base)
{
register const unsigned char *s = (const unsigned char *)nptr;
register unsigned long acc;
register int c;
register unsigned long cutoff;
register int neg = 0, any, cutlim;
/*
* See strtol for comments as to the logic used.
*/
do {
c = *s++;
} while (isspace(c));
if (c == '-') {
neg = 1;
c = *s++;
} else if (c == '+')
c = *s++;
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X')) {
c = s[1];
s += 2;
base = 16;
}
if (base == 0)
base = c == '0' ? 8 : 10;
cutoff = (unsigned long)ULONG_MAX/(unsigned long)base;
cutlim = (unsigned long)ULONG_MAX % (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 = ULONG_MAX;
rptr->_errno = ERANGE;
} else if (neg)
acc = -acc;
if (endptr != 0)
*endptr = (char *) (any ? (char *)s - 1 : nptr);
return (acc);
}
함수 정의에서 시작하여 우리의 응용 프로그램이 단일 스레드 인 경우 제거 할 수있는 재진입 관행이 있음을 알 수 있습니다. 우리가 필요로하지 않는 문자열을 가리키는 포인터를 저장하는 char ** ptr 인자도 있습니다. 또한 길이 정의가 없기 때문에 문자열의 길이를 찾기 위해 널 문자를 검색해야합니다.
이 응용 프로그램에서 * s는 내 플랫폼에서 의미가 있지만 사용자의 것 같지 않은 레지스터로 정의됩니다. 우리가 필요로하지 않는 다른 정수도 정의되어 있습니다.
do/while 루프에는 공백, 가로 탭, 줄 바꿈, 세로 탭, 피드 및 캐리지 리턴 문자를 확인하는 isspace()
이 호출됩니다. 공간 만 있으면됩니다. 또한 현의 앞에서 시작하여 뒤로 돌아갑니다. 우세하게 작은 숫자가 있다면 그것을 바꿔라.
그런 다음 기본 테스트를 수행합니다. 베이스는 0이 될 수 있으며,베이스 (사이클을 필요로 함)를 자동으로 감지 할 수 있으며, 8 또는 16이면 앞에있는 '0'또는 '0x'를 앞뒤로 입력 할 수 있습니다.
다음으로 'cutoff'및 'cutlim'변수를 만듭니다. 표면 상으로는 범위 검사가 필요 없기 때문에 이들을 필요로하지 않습니다.
마지막으로 for 루프를 종료합니다. 문자 유형과 숫자 값을 결정하는 if \ else if \ else 블록이 있으며 isdigit
, isalpha
및 isupper
함수가 있습니다. 이것들은 약간의 로케일 종속적 인 코드를 포함합니다; if/else if/else 블록 전체를 하나의 c -= 0
문으로 대체하는 10 진수 값을 가정 할 수 있습니다.
다음으로 오류 검사에 대한 자세한 내용은 if (c >= base)
이며 ischeap은 유지 보수에 도움이 될 수 있습니다. C는 부호가 없으므로, 예를 들어 *가 '0', 0x30보다 작은 공백 (0x20) 인 경우이 값은 (부호가없는) (0x30 - 0x20) = 255 - 10으로 계산됩니다. 베이스 (10)보다 크다. 그것은 완벽하지는 않지만 꽤 좋고 값이 쌉니다.
다음으로 if (any...
블록을 검사하는 경계가 있습니다. 그런 다음 함수의 실제 고기 (acc *= base; acc += c;
)가 표시됩니다.이것을 최적화하기 위해 할 수있는 방법은 거의 없지만, 바이너리 기반이 있다면이 값을 교대로 변환 할 수 있습니다. 다행히 Arduino ISR이라면 프로세서에 빠른 하드웨어 멀티 플라이어를 사용하는 것이 좋을 것입니다. 멀티 플라이 축적 (multiply-accumulate)과 같은 DSP 어셈블리 명령어를 살펴보면 속도 향상을 기대할 수 있습니다.
for 루프 이후에 우리가 무시할 수있는 음수의 오류 처리와 처리가 더 있습니다.
그래서, 요약, 당신은 그것을 많이하고 있다면 나는 당신의 특별한 경우를 처리 할 수있는 새로운 기능을 써서 :
atoi
의들이 generic을 받아 unsigned long TwelveCharDecimalStringWithLeadingSpacestoul(char *nptr)
{
register const unsigned char *s = (const unsigned char *)nptr;
register unsigned long acc;
register int c, base = 10;
do {
c = *s++;
} while (c == ' ');
for (acc = 0;; c = *s++) {
c -= '0';
if (c >= base) {
_errno = ERANGE;
acc = -1;
break;
}
acc *= base;
acc += c;
}
return (acc);
}
은 가정을 사용하는 당신 좀 더 빨라졌습니다.
unsigned long result = 0;
char *begin = strrchr(buf, ' ');
result = strtoul(buf, NULL, 10);
if (result == 0 && errno == ERANGE)
// Handle error
편집 :이 작업이 엄청 많이 일어난다, 또는 매우 빠르게 발생하는 않는 한, 당신은 훨씬 간단, 명확하게,보다 안전하고 유연하며 일반적으로 더 나은 형편이 아마 더 좋을 것 같아 나는 글쓰기를 마치고 FredOverflow has posted a better answer에 주목합니다. 루프를 실행하면 (필자는 그렇게하지 않았지만 필요하지는 않지만 필요한 경우 알려진 루프가 반복 될 수 있음) & 15
이라는 깔끔한 트릭을 사용합니다. 그러나, 위의 함수는 일반적인 경우에 일부 표준 라이브러리 호출 속도를 높이는 방법에 대한 접근 방법을 보여줍니다.
'문자열'을 사용할 수 없습니까? –
'atoi'는 선행 공백을 건너 뜁니다. 왜 당신의 루프가 그것보다 더 빠를까요? –
@JamesKanze - 아마 그 숫자가 대개 6 자 미만이라는 것을 알고있을 것입니다. 그래서 문자열의 끝에서부터 시작하는 것이 더 빠릅니까? 또한 'leading spaces'는 아마도'''에 대한 테스트보다 확실히 느린'isspace()'에 대한 호출 일 것입니다. –