題目:輸入一個表示整數的字串,把該字串轉換成整數並輸出。例如輸入字串"345",則輸出整數345。 分析:這道題儘管不是很難,學過C/C++語言一般都能實現準系統,但不同程式員就這道題寫出的代碼有很大區別,可以說這道題能夠很好地反應出程式員的思維和編程習慣,因此已經被包括微軟在內的多家公司用作面試題。建議讀者在往下看之前自己先編寫代碼,再比較自己寫的代碼和下面的參考代碼有哪些不同。
首先我們分析如何完成準系統,即如何把表示整數的字串正確地轉換成整數。還是以"345"作為例子。當我們掃描到字串的第一個字元'3'時,我們不知道後面還有多少位,僅僅知道這是第一位,因此此時得到的數字是3。當掃描到第二個數字'4'時,此時我們已經知道前面已經一個3了,再在後面加上一個數字4,那前面的3相當於30,因此得到的數字是3*10+4=34。接著我們又掃描到字元'5',我們已經知道了'5'的前面已經有了34,由於後面要加上一個5,前面的34就相當於340了,因此得到的數字就是34*10+5=345。
分析到這裡,我們不能得出一個轉換的思路:每掃描到一個字元,我們把在之前得到的數字乘以10再加上當前字元表示的數字。這個思路用迴圈不難實現。
由於整數可能不僅僅之含有數字,還有可能以'+'或者'-'開頭,表示整數的正負。因此我們需要把這個字串的第一個字元做特殊處理。如果第一個字元是'+'號,則不需要做任何操作;如果第一個字元是'-'號,則表明這個整數是個負數,在最後的時候我們要把得到的數值變成負數。
接著我們試著處理非法輸入。由於輸入的是指標,在使用指標之前,我們要做的第一件是判斷這個指標是不是為空白。如果試著去訪問null 指標,將不可避免地導致程式崩潰。另外,輸入的字串中可能含有不是數位字元。每當碰到這些非法的字元,我們就沒有必要再繼續轉換。最後一個需要考慮的問題是溢出問題。由於輸入的數字是以字串的形式輸入,因此有可能輸入一個很大的數字轉換之後會超過能夠表示的最大的整數而溢出。
現在已經分析的差不多了,開始考慮編寫代碼。首先我們考慮如何聲明這個函數。由於是把字串轉換成整數,很自然我們想到:
int StrToInt(const char* str);
這樣聲明看起來沒有問題。但當輸入的字串是一個null 指標或者含有非法的字元時,應該返回什麼值呢?0怎麼樣?那怎麼區分非法輸入和字串本身就是”0”這兩種情況呢?
接下來我們考慮另外一種思路。我們可以返回一個布爾值來指示輸入是否有效,而把轉換後的整數放到參數列表中以引用或者指標的形式傳入。於是我們就可以聲明如下:
bool StrToInt(const char *str, int& num);
這種思路解決了前面的問題。但是這個函數的使用者使用這個函數的時候會覺得不是很方便,因為他不能直接把得到的整數賦值給其他整形變臉,顯得不夠直觀。
前面的第一種聲明就很直觀。如何在保證直觀的前提下當碰到非法輸入的時候通知使用者呢?一種解決方案就是定義一個全域變數,每當碰到非法輸入的時候,就標記該全域變數。使用者在調用這個函數之後,就可以檢驗該全域變數來判斷轉換是不是成功。
下面我們寫出完整的實現代碼。參考代碼:
enum Status {kValid = 0, kInvalid};
int g_nStatus = kValid;
///////////////////////////////////////////////////////////////////////
// Convert a string into an integer
///////////////////////////////////////////////////////////////////////
int StrToInt(const char* str)
{
g_nStatus = kInvalid;
long long num = 0;
if(str != NULL)
{
const char* digit = str;
// the first char in the string maybe '+' or '-'
bool minus = false;
if(*digit == '+')
digit ++;
else if(*digit == '-')
{
digit ++;
minus = true;
}
// the remaining chars in the string
while(*digit != '/0')
{
if(*digit >= '0' && *digit <= '9')
{
num = num * 10 + (*digit - '0');
// overflow
if(num > std::numeric_limits<int>::max())
{
num = 0;
break;
}
digit ++;
}
// if the char is not a digit, invalid input
else
{
num = 0;
break;
}
}
if(*digit == '/0')
{
g_nStatus = kValid;
if(minus)
num = 0 - num;
}
}
return static_cast<int>(num);
}
討論:在參考代碼中,我選用的是第一種聲明方式。不過在面試時,我們可以選用任意一種聲明方式進行實現。但當面試官問我們選擇的理由時,我們要對兩者的優缺點進行評價。第一種聲明方式對使用者而言非常直觀,但使用了全域變數,不夠優雅;而第二種思路是用傳回值來表明輸入是否合法,在很多API中都用這種方法,但該方法聲明的函數使用起來不夠直觀。
最後值得一提的是,在C語言提供的庫函數中,函數atoi能夠把字串轉換整數。它的聲明是int atoi(const char *str)。該函數就是用一個全域變數來標誌輸入是否合法的。
----------------------------------------------------------------------------------------------------
C語言提供了幾個標準庫函數,可以將任意類型(整型、長整型、浮點型等)的數字轉換為字串。以下是用itoa()函數將整數轉 換為字串的一個例子:
# include <stdio.h>
# include <stdlib.h>
void main (void)
{
int num = 100;
char str[25];
itoa(num, str, 10);
printf("The number ’num’ is %d and the string ’str’ is %s. /n" ,
num, str);
}
itoa()函數有3個參數:第一個參數是要轉換的數字,第二個參數是要寫入轉換結果的目標字串,第三個參數是轉移數字時所用 的基數。在上例中,轉換基數為10。10:十進位;2:二進位...
itoa並不是一個標準的C函數,它是Windows特有的,如果要寫跨平台的程式,請用sprintf。
是Windows平台下擴充的,標準庫中有sprintf,功能比這個更強,用法跟printf類似:
char str[255];
sprintf(str, "%x", 100); //將100轉為16進位表示的字串。
下列函數可以將整數轉換為字串:
----------------------------------------------------------
函數名 作 用
----------------------------------------------------------
itoa() 將整型值轉換為字串
itoa() 將長整型值轉換為字串
ultoa() 將無符號長整型值轉換為字串
一 atoi 把字串轉換成整型數
常式序:
#include <ctype.h>
#include <stdio.h>
int atoi (char s[]);
int main(void )
{
char s[100];
gets(s);
printf("integer=%d/n",atoi(s));
return 0;
}
int atoi (char s[])
{
int i,n,sign;
for(i=0;isspace(s[i]);i++)//跳過空白符
;
sign=(s[i]==’-’)?-1:1;
if(s[i]==’+’||s[i]==’ -’)//跳過符號
i++;
for(n=0;isdigit(s[i]);i++)
n=10*n+(s[i]-’0’);//將數字字元轉換成整形數字
return sign *n;
}
二 itoa 把一整數轉換為字串
常式序:
#include <ctype.h>
#include <stdio.h>
void itoa (int n,char s[]);
//atoi 函數:將s轉換為整形數
int main(void )
{
int n;
char s[100];
printf("Input n:/n");
scanf("%d",&n);
printf("the string : /n");
itoa (n,s);
return 0;
}
void itoa (int n,char s[])
{
int i,j,sign;
if((sign=n)<0)//記錄符號
n=-n;//使n成為正數 i=0;
do{
s[i++]=n%10+’0’;//取下一個數字
}while ((n/=10)>0);//刪除該數字
if(sign<0)
s[i++]=’-’;
s[i]=’/0’;
for(j=i;j>=0;j--)//產生的數字是逆序的,所以要逆序輸出
printf("%c",s[j]);
}
------------------------------------------------------------------------------------
自己寫的atoi函數----(注意:自己定義的atoi函數和庫的atoi函數一樣的時候,拋出異常時會引起異常退出,個人認為是異常沒有不知道被那個函數拋出,所以coredump)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <iostream>
#include <string>
#include <exception>
using namespace std;
const unsigned int SIGN_BIT = 0x1 << 31;
bool isDigit(const char ch)
{
if (ch <= '9' && ch >= '0')
{
return true;
}
return false;
}
int atoi_i(const char *str)
{
assert(str != NULL);
while (' ' == *str){ str++; }
int result = 0;
bool signFlag = false;
if ('+' == *str)
{
if (false == isDigit(*++str)) throw "input format error!";
}
else if ('-' == *str)
{
if (false == isDigit(*++str)) throw "input format error!";
signFlag = true;
}
else if (*str > '9' || *str < '0')
{
throw "input format error!";
}
do
{
result = result * 10 + *str++ - '0';
if ((result & SIGN_BIT) != 0)
{
throw "overflow error!";
}
}
while (isDigit(*str));
if (true == signFlag)
{
result = -result;
}
return result;
}
int main(int argc, char *argv[])
{
char input[1024];
while (1)
{
try
{
cout << "Input Array:";
cin >> input;
printf("exchange:%d/n", atoi_i(input));
}
catch (const char *p)
{
cout <<"Error Info:" << p << endl;
}
catch ( ... )
{
cout << "test" << endl;
}
}
return 0;
}
-----------------------------------------------------------------------------------
itoa函數(自己)
#define SWAP(X,Y) (X) = (X) ^ (Y); (Y) = (X) ^ (Y); (X) = (X) ^ (Y)
char *itoa_i(int src, char *a)
{
bool signFlag = false;
if (src < 0)
{
src = -src;
signFlag = true;
}
int k = 0;
do
{
a[k++] = src % 10 + '0';
}
while((src /= 10) > 0);
if (true == signFlag)
{
a[k++] = '-';
}
a[k] = '/0';
for (int i = 0, j = k - 1; i < j; ++i, --j)
{
SWAP(*(a+i),*(a+j));
}
return a;
}