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;}

發現編譯出現了警告,如下:

Advertisements

[[email protected] ctest]# gcc -g -o test test.ctest.c: In function 'reverseInt':test.c:24:2: warning: function returns address of local variable [-Wreturn-local-addr]  return str; ^

從編譯器給出的信息很清楚的說明了問題:返回了一個局部變數的地址,但是我們知道,函數的局部變數是存在stack中的,當這個函數調用過程結束時,這個局部變數都是要釋放掉的,自然就不可再使用了,所以就會產生這樣的warning,這個是和變數的生命周期相關的。

Advertisements

版本二

對於版本一存在的問題,很自然的會想到有兩種解決方案,第一:使用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;}

運行結果如下:

[[email protected] 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;}

運行,能得到我們期望的結果了:

[[email protected] ctest]# ./test [test_data1] 12345--->54321[test_data2] 789--->987

其實,版本三還有很多細節需要考慮的,比如:當輸入的整數超過int的範圍如何處理等等,雖然是小細節,但卻十分重要,大家有興趣可以思考下練練手。

Advertisements

你可能會喜歡