1. 程式人生 > >關於C語言中返回區域性指標變數

關於C語言中返回區域性指標變數

前言

寫這篇文章,是因為同學在leetcode上遇到了這樣一個錯誤:
- 題目:Two Sum
- 程式碼如下:

int* twoSum(int* nums, int numsSize, int target)
{
    int i,j;
    int res[2];                                 /* 加上static之後正常執行 */
    for (i=0; i<numsSize; i++) {
        for (j=i; j<numsSize; j++) {
            if (nums[i]+nums[j]==target) {
                res[0]=i;
                res[1]=j;
                break;
            } else {
                continue;
            }
        }
    }
    return res;
}

- 執行時出現錯誤:

load of null pointer of type 'const int'

- Google之後發現,在宣告res[2]的時候加上static就可以正常執行。於是在《The C Programming Language》(P70,4.6 靜態變數)找到了相關內容:

用static限定外部變數與函式,可以將其後宣告的物件的作用域限定為被編譯原始檔的剩餘部分。該外部變數/函式除了對該所在的檔案可見外,其他檔案都無法訪問。
用static宣告內部變數,則該變數是某個特定函式的區域性變數,只能在該函式中使用。但它與自動變數不同的是,不管其所在函式是否被呼叫,它一直存在,而不像自動變數那樣,隨著所在函式的被呼叫和退出而存在和消失。換句話說,static型別的內部變數是一種只能在某個特定函式中使用但一直佔據儲存空間的變數。

分析上面的程式碼,函式返回了一個int型別的指標,但是報錯卻說是一個空指標。猜想是int res[2];的宣告導致res是一個自動變數的陣列指標,在退出該函式之後,res指標指向的記憶體被覆蓋,所以導致出錯。使用static進行宣告,可以保證res[2]不會因為函式的退出而消失。下面進行驗證。

驗證

  • 平臺和工具

    • Windows 10, Code::Blocks-16.01(mingw)
  • 測試程式碼

    
    #include <stdio.h>
    
    char *test1(void)
    {
        /*
         * 編譯時出現warning,執行時列印亂碼。
         * char b[10] 是區域性變數,其存放在棧上,在函式執行完成就會被釋放。
         * 而返回它存放的地址,此時已經是可能是個非法地址。
         * 執行時列印亂碼,驗證了b[10]的記憶體被釋放。
         */
    char b[10] = "abcde"; return b; // warning: function returns address of local variable [-Wreturn-local-addr]| } char *test1_ver2(void) { /* * 正確執行。 * 定義static char b[10]; 將其存放在靜態儲存區,只有在程式執行結束後才會被系統回收。 * 即使退出該函式,b[]陣列存放的靜態儲存區始終存在,所以,我們再對其進行訪問,不會出現錯誤。 */ static char b[10] = "abcde"; return b; } char *test2(void) { /* * 可以執行,但打印出錯。 * c[10]和*pStr均為自動變數,儲存在棧中;函式返回時,c[]陣列和pStr指標的記憶體空間被釋放 * 所以,在外部訪問c[]陣列時,指向的記憶體空間無效。 */ char c[10] = "abcde"; char *pStr = c; return pStr; } char *test3(void) { /* * 正確執行。 * 指標c儲存在棧中,但是"abcdef"字串儲存在常量區,其記憶體空間直到程式執行結束才會被釋放。 * 所以,返回的地址是一個實際存在的有效地址。 */ char *c = "abcdef"; return c; } int main() { char *p_test1, *p_test1_ver2, *p_test2, *p_test3; p_test1 = test1(); p_test1_ver2 = test1_ver2(); p_test2 = test2(); p_test3 = test3(); printf("test1:%s\n", p_test1); printf("test1_ver2:%s\n", p_test1_ver2); printf("test2:%s\n", p_test2); printf("test3:%s\n", p_test3); return 0; }
  • 輸出結果1:

    test1:[w骾      A
    test1_ver2:abcde
    test2:`
    test3:abcdef
  • 輸出結果2:

    test1:[w詡?
    test1_ver2:abcde
    test2:`
    test3:abcdef