由一道面試題所引出的C語言static變量特性
最近部門在準備春招筆試題時,有這樣一道題目:用C/C++語言實現(xiàn)一個函數(shù),給定一個int類型的整數(shù),函數(shù)輸出逆序的整數(shù)對應的字符串,例如輸入1234,則輸出字符串"4321",,輸入-1234,則輸出字符串"-4321"。題目要求,不使用標準庫,以及不能分配動態(tài)內存。當時覺得蠻簡單的,這不就是類似字符串逆轉嘛,紙上得來終覺淺,絕知此事要躬行,自己嘗試做了一下,發(fā)現(xiàn)還是有一些地方值得注意。今天在此整理一下常見的坑,鞏固下基礎東西。
版本一
算法思路其實很簡單:使用對10取余和除法操作依次獲取每一位的數(shù)字,然后根據(jù)ASSIC碼轉換為字符,將結果存放在一個char型數(shù)組中,***返回字符串數(shù)組結果,如下所示:
- #include<stdio.h>
- //版本一
- const char * reverseInt(int n)
- {
- char str[16] = {0};
- int temp = n;
- int i = 0;
- if (n < 0)
- {
- temp = -n;
- str[i++] = '-';
- }
- //當temp除到是一位數(shù)的時候退出
- while (0 != temp / 10)
- {
- char ch = temp % 10 + 48;
- temp = temp / 10;
- str[i++] = ch;
- }
- //處理原始數(shù)據(jù)的***位
- str[i++] = temp % 10 + 48;
- return str;
- }
- int main(int argc, char **agrv)
- {
- int test_data1 = 12345;
- int test_data2 = 789;
- printf("[test_data1] %d--->%s\n",
- test_data1, reverseInt(test_data1));
- printf("[test_data2] %d--->%s\n",
- test_data2, reverseInt(test_data2));
- return 0;
- }
發(fā)現(xiàn)編譯出現(xiàn)了警告,如下:
- [root@epc.baidu.com ctest]# gcc -g -o test test.c
- test.c: In function 'reverseInt':
- test.c:24:2: warning: function returns address of local variable [-Wreturn-local-addr]
- return str; ^
從編譯器給出的信息很清楚的說明了問題:返回了一個局部變量的地址,但是我們知道,函數(shù)的局部變量是存在stack中的,當這個函數(shù)調用過程結束時,這個局部變量都是要釋放掉的,自然就不可再使用了,所以就會產(chǎn)生這樣的warning,這個是和變量的生命周期相關的。
版本二
對于版本一存在的問題,很自然的會想到有兩種解決方案,***:使用malloc分配動態(tài)內存存放結果,但是題目中明確說明不能不能分配動態(tài)內存。因此自然排除掉。第二種方案就是將char result[16]改為static型:static char result[16];對,就是這么一點改動。
- #include<stdio.h>
- //版本二
- const char * reverseInt(int n)
- {
- static char str[16] = {0};
- int temp = n;
- int i = 0;
- if (n < 0)
- {
- temp = -n;
- str[i++] = '-';
- }
- //當temp除到是一位數(shù)的時候退出
- while (0 != temp / 10)
- {
- char ch = temp % 10 + 48;
- temp = temp / 10;
- str[i++] = ch;
- }
- //處理原始數(shù)據(jù)的***位
- str[i++] = temp % 10 + 48;
- return str;
- }
- int main(int argc, char **agrv)
- {
- int test_data1 = 12345;
- int test_data2 = 789;
- printf("[test_data1] %d--->%s\n",
- test_data1, reverseInt(test_data1));
- printf("[test_data2] %d--->%s\n",
- test_data2, reverseInt(test_data2));
- return 0;
- }
運行結果如下:
- [root@epc.baidu.com ctest]# ./test
- [test_data1] 12345--->54321
- [test_data2] 789--->98721
從運行結果上看,***個測試數(shù)據(jù)其結果是正確的,但是第二個輸出結果確實錯誤的。這是什么原因?先來回一下用static修飾所修飾的局部變量(也稱靜態(tài)局部變量)特點,如下:
1:靜態(tài)局部變量定義時未賦初值,則默認初始化為0;
2:靜態(tài)局部變量其作用域為函數(shù)或代碼塊,其生命周期為整個程序的運行期間;注意這兩個概念不要混淆;
3:在一個進程的運行期間,靜態(tài)局部變量只會初始化一次,就是***次調用該靜態(tài)局部變量所在函數(shù)的時候初始化,此后再調用不會初始化。
好了,到這里,其實問題的原因已經(jīng)很明顯了:在上面程序中,static char str[16] = {0}只會初始化一次,既在執(zhí)行reverseInt(test_data1)時初始化,執(zhí)行完該語句,將結果存放到str中,此時str中的內容為54321,既str[16] = {'5','4','3','2','1','\0'};當再次對第二個測試數(shù)進行轉換調用reverseInt(test_data2)時,str仍然是上次的結果{'5','4','3','2','1','\0'},因此在轉換后為98721。
版本三
那么如何解決版本二的問題了,一個很簡單的辦法就是在reverseInt函數(shù)中對static變量str每次使用for循環(huán)進行初始化,如下,鑒于篇幅,就不將main函數(shù)也貼出來了:
- const char * reverseInt(int n)
- {
- static char str[16] = {0};
- int temp = n;
- int i = 0;
- int j = 0;
- for (; j < 16; j++)
- {
- str[j] = '\0';
- }
- if (n < 0)
- {
- temp = -n;
- str[i++] = '-';
- }
- //當temp除到是一位數(shù)的時候退出
- while (0 != temp / 10)
- {
- char ch = temp % 10 + 48;
- temp = temp / 10;
- str[i++] = ch;
- }
- //處理原始數(shù)據(jù)的***位
- str[i++] = temp % 10 + 48;
- return str;
- }
運行,能得到我們期望的結果了:
- [root@epc.baidu.com ctest]# ./test
- [test_data1] 12345--->54321
- [test_data2] 789--->987
其實,版本三還有很多細節(jié)需要考慮的,比如:當輸入的整數(shù)超過int的范圍如何處理等等,雖然是小細節(jié),但卻十分重要,大家有興趣可以思考下練練手。