C庫函數-strtol()
概述
strtol()是C庫函數,其功能是用於將字符串轉換成整數。函數原型爲
long int strtol(const char *str,char **endptr,int base)
該函數將str所指向的字符串根據給定的base轉換爲一個long int型的長整數,base必須介於2和36之間,或者是特殊值0。
參數
- str:要轉換爲長整數的字符串
- endptr:對類型爲char* 的對象的引用,其值由函數設置爲str中數值後的下一個字符。
- base:基數,必須介於2和36之間,或者是特殊值0。base代表的採用的進制方式。如base值爲2就代表採用的二進制。當base值爲0時則是採用10進製做轉換。當遇到"0x"前置字符則會採用16進製做轉換。
功能
- 開始時,strtol會掃描nptr所指向的字符串,這時它會跳過非法字符,如:空格。直到遇見數字或者"+、—“號纔開始轉換,再遇到非數字或者”\0"時結束轉換。並將結果返回(返回長整型的整數)。
返回值
- 返回轉換之後的長整型數,否則對異常返回並且設置errno。
幾點說明
- strtol會從nptr所指向字符串的頭不開始查找,當遇到數字或"+、-"時就開始轉換,遇到其他的字符則停止轉換並返回。如果字符串中穿插着兩串數字,則只會對第一串數字進行轉換。
- 如果endptr不是NULL,strtol()會將第一個無效的字符的地址方到*enptr。
- 如果字符串中沒有數字,strtol()會將nptr的初始值,存儲到endptr中,並且返回0。
例程
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int base;
char *endptr, *str;
long val;
if (argc < 2) {
fprintf(stderr, "Usage: %s str [base]/n", argv[0]);
/*目的是從命令行獲取字符串,如果在命令行沒有輸入字符串則會退出。*/
exit(EXIT_FAILURE);
}
str = argv[1]; //獲取命令行的第一個字符串
base = (argc > 2) ? atoi(argv[2]) : 10;
/*如果命令行沒有輸入base值,則默認爲10,即按照十進制進行輸出。atoi()函數是將字符串轉換成相應的整數*/
errno = 0;
/*對調用結束之後的結果進行分析(避免'0'的影響),因爲錯誤和字符傳中只含有0時返回值都是0,但錯誤時會重置errno,此時就可以通過errno來區分二者*/
val = strtol(str, &endptr, base);
/* 檢查不同的錯誤*/
if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) {
perror("strtol");
exit(EXIT_FAILURE);
}
if (endptr == str) {
fprintf(stderr, "No digits were found/n");
exit(EXIT_FAILURE);
}
/* If we got here, strtol() successfully parsed
a number */
printf("strtol() returned %ld/n", val);
/* Not necessarily an error... */
if (*endptr != '/0')
printf("Further characters after number: %s/n", endptr);
exit(EXIT_SUCCESS);
}
<注:上面這段程序是Linux中strtol()函數幫助文檔中一個例子程序,我在相應的部分添加了註釋>
- 該函數功能和使用其實還是挺簡單的,多敲幾個例子基本就可以摸清楚怎麼玩兒。
源碼
下面附上該函數實現的源碼,有興趣的同學可以琢磨一下。
long strtol(const char * restrict nptr, char ** restrict endptr, int base)
{
const char *s;
unsigned long acc;
char c;
unsigned long cutoff;
int neg, any, cutlim;
s = nptr;
do {
c = *s++;
} while (isspace((unsigned char)c));
if (c == '-') {
neg = 1;
c = *s++; //去掉前導空格和+ - 符號
} else {
neg = 0;
if (c == '+')
c = *s++;
}
//判斷進制,並去除前導0x或者0
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X') &&
((s[1] >= '0' && s[1] <= '9') ||
(s[1] >= 'A' && s[1] <= 'F') ||
(s[1] >= 'a' && s[1] <= 'f'))) {
c = s[1];
s += 2;
base = 16;
}
if (base == 0)
base = c == '0' ? 8 : 10;
acc = any = 0;
if (base < 2 || base > 36)
goto noconv;
/*判斷溢出的方法:
cutoff爲系統能夠表示的最大數除以base的結果,也就是當前進制能夠表示的最大有效的數。
例如32爲系統下長整形的範圍是[-2147483648..2147483647],如果base是10的話,則cutoff就是
214748364,而cutlim就是7(正整數)或者8(負整數)。如果當前算得的值大於cutoff就溢出了,或者等於cutoff但是下一位大於cutlim也就溢出了
*/
cutoff = neg ? (unsigned long)-(LONG_MIN + LONG_MAX) + LONG_MAX
: LONG_MAX;
cutlim = cutoff % base;
cutoff /= base;
for ( ; ; c = *s++) {
if (c >= '0' && c <= '9')
c -= '0';
else if (c >= 'A' && c <= 'Z')
c -= 'A' - 10;
else if (c >= 'a' && c <= 'z')
c -= 'a' - 10;
else
break;
if (c >= base)
break;
//如果溢出則設置any爲負數
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 (!any) {
noconv:
errno = EINVAL;
} else if (neg)
acc = -acc;
if (endptr != NULL)
*endptr = (char *)(any ? s - 1 : nptr);
return (acc);
}
<注:其中關鍵字restrict是C99引入的,它只用於限定和約束指針並標明指針是訪問一個數據對象的唯一且初始的方式,**即它告訴編譯器,所有修改該指針所指向內存中內容的操作都必須通過該指針來修改,而不能通過其他途徑(其他變量或指針)來修改;**這樣做的好處是:**能幫助編譯器進行更好的有優化代碼,生成更有效率的彙編代碼。**如上面char * restrict nptr表明ptr指向的內存單元只能由訪問到,其他任何同樣指向這個內存單元的其他指針都是未定義的,即無效指針(野指針)>