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