淺談C語言函數返回值--局部變量和局部變量地址

下面的內容是在C專家編程裏面看到的,摘錄於此。

在C語言中,局部變量的作用域只在函數內部,在函數返回後,局部變量的內存就會被釋放。如果函數只是返回局部變量,那麼這個局部變量會被複制一份傳回被調用處。但是如果函數返回的是局部變量的地址,那麼就會報錯,因爲函數只是把指針複製後返回了,但是指針指向的內容已經被釋放,這樣指針指向的內容就是不可預料的內容,程序就會出錯。準確的來說,函數不能通過返回指向棧內存的指針(返回指向堆內存的指針是可以的)。

先來看一個函數返回局部變量的例子

    #include <stdio.h>
     
    int fun()
    {
        int num = 100;
        num = num + 100;
        
        return num;
    }
    int main()
    {
        int num;
        
        num = fun();
        
        printf("%d\n", num);
     
        return 0;
    }

fun函數返回一個int的局部變量,函數會把局部的num的值複製一份拷貝給主函數裏面的num。這樣是可以的,而且這種方式在程序裏面還是經常用到的。上面程序輸出:200

下面函數返回局部變量地址

    #include <stdio.h>
     
    char *fun()
    {
        char buffer[20];
        int i;
        for(i = 0; i < sizeof(buffer)-1; i++)
            buffer[i] = 'a';
        buffer[i] = '\0';
        return buffer;
    }
    int main()
    {
        char *str;
        str = fun();
        printf("%s\n", str);
        return 0;
    }

編譯運行:

編譯警告程序返回局部變量地址,輸出爲亂碼。因爲fun返回的是局部變量的地址,真是拷貝了一份地址,地址所指向的內容在fun結束的時候已經釋放,變量已經被銷燬,現在根本不知道地址指向的內容的是什麼。

如果確實要返回一個局部變量的地址應該怎麼做,解決這個問題有下面幾種方案。

1、返回一個字符串常量的指針

    #include <stdio.h>
     
    char *fun()
    {
        char *buffer = "aaaaaaaaaa";
        return buffer;
    }
    int main()
    {
        char *str;
        str = fun();
        printf("%s\n", str);
        return 0;
    }

編譯運行

這樣程序運行是沒有問題的,buffer存在只讀內存區,在fun退出的時候,字符串常量不會被收回,因此把地址賦給str時可以正確訪問。上面這個方式只是最簡單的解決方案,因爲字符串存放在只讀內存區,以後需要修改它的時候就會很麻煩。

2、使用全局聲明的數組。

    #include <stdio.h>
     
    char buffer[20];
        
    char *fun()
    {
        int i;
        for(i = 0; i < sizeof(buffer)-1; i++)
            buffer[i] = 'a'+ i;
        buffer[i] = '\0';
        return buffer;
    }
    int main()
    {
        char *str;
        str = fun();
        printf("%s\n", str);
        return 0;
    }

編譯運行

這種情況適用於自己創建字符串,而且簡單容易。缺點就是任何人都有可能在任何時候修改這個全局數組,而且該函數的下一次調用會覆蓋數組的內容。

3、使用靜態數組

    #include <stdio.h>
     
    char *fun()
    {
        int i;
        static char buffer[20];
        for(i = 0; i < sizeof(buffer)-1; i++)
            buffer[i] = 'a'+ i;
        buffer[i] = '\0';
        return buffer;
    }
    int main()
    {
        char *str;
        str = fun();
        printf("%s\n", str);
        return 0;
    }

編譯運行


使用靜態數組可以保證內存不被回收,而且可以防止任何人修改這個數組。只有擁有指向該數組的指針的函數才能修改這個靜態數組,不過同時該函數的下一次調用會覆蓋數組的內容。同時和全局數組一樣,大型緩衝區閒置是非常浪費空間的。

4、顯示的分配內存,在堆上動態分配內存。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    char *fun()
    {
        int i;
        char *buffer = (char *)malloc(sizeof(char) * 20);  
        strcpy(buffer, "abcdefg");
        return buffer;
    }
    int main()
    {
        char *str;
        str = fun();
        printf("%s\n", str);
        return 0;
    }


這個方法具有靜態數組的方法,而且每次調用都創建一個新的緩衝區,不會覆蓋以前的內容。適用於多線程代碼。缺點是程序員必須承擔內存的管理,這項任務可能很複雜,很容易產生內存泄露或者尚在使用就被釋放。

5、最好的解決辦法就是調用者分配內存來保存函數的返回值,同時指定緩衝區的大小

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    void fun(char *str, int size)
    {
        char *s = "abcdefghijklmnopq";
        strncpy(str, s, size);
    }
    int main()
    {
        int size = 10;
        char *str = (char *)malloc(sizeof(char) * size);  
        fun(str, size);
        printf("%s\n", str);
        free(str);
        return 0;
    }

編譯運行


程序員在同一塊代碼中同時進行malloc和free操作,內存管理最安全,方便。
 

發佈了23 篇原創文章 · 獲贊 3 · 訪問量 3723
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章