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


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

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

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

  1.  
    #include <stdio.h>
  2.  
     
  3.  
    int fun()
  4.  
    {
  5.  
    int num = 100;
  6.  
    num = num + 100;
  7.  
     
  8.  
    return num;
  9.  
    }
  10.  
    int main()
  11.  
    {
  12.  
    int num;
  13.  
     
  14.  
    num = fun();
  15.  
     
  16.  
    printf("%d\n", num);
  17.  
     
  18.  
    return 0;
  19.  
    }

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

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

  1.  
    #include <stdio.h>
  2.  
     
  3.  
    char *fun()
  4.  
    {
  5.  
    char buffer[20];
  6.  
    int i;
  7.  
    for(i = 0; i < sizeof(buffer)-1; i++)
  8.  
    buffer[i] = 'a';
  9.  
    buffer[i] = '\0';
  10.  
    return buffer;
  11.  
    }
  12.  
    int main()
  13.  
    {
  14.  
    char *str;
  15.  
    str = fun();
  16.  
    printf("%s\n", str);
  17.  
    return 0;
  18.  
    }

編譯運行:

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

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

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

  1.  
    #include <stdio.h>
  2.  
     
  3.  
    char *fun()
  4.  
    {
  5.  
    char *buffer = "aaaaaaaaaa";
  6.  
    return buffer;
  7.  
    }
  8.  
    int main()
  9.  
    {
  10.  
    char *str;
  11.  
    str = fun();
  12.  
    printf("%s\n", str);
  13.  
    return 0;
  14.  
    }

編譯運行

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

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

  1.  
    #include <stdio.h>
  2.  
     
  3.  
    char buffer[20];
  4.  
     
  5.  
    char *fun()
  6.  
    {
  7.  
    int i;
  8.  
    for(i = 0; i < sizeof(buffer)-1; i++)
  9.  
    buffer[i] = 'a'+ i;
  10.  
    buffer[i] = '\0';
  11.  
    return buffer;
  12.  
    }
  13.  
    int main()
  14.  
    {
  15.  
    char *str;
  16.  
    str = fun();
  17.  
    printf("%s\n", str);
  18.  
    return 0;
  19.  
    }

編譯運行

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

3、使用靜態數組

  1.  
    #include <stdio.h>
  2.  
     
  3.  
    char *fun()
  4.  
    {
  5.  
    int i;
  6.  
    static char buffer[20];
  7.  
    for(i = 0; i < sizeof(buffer)-1; i++)
  8.  
    buffer[i] = 'a'+ i;
  9.  
    buffer[i] = '\0';
  10.  
    return buffer;
  11.  
    }
  12.  
    int main()
  13.  
    {
  14.  
    char *str;
  15.  
    str = fun();
  16.  
    printf("%s\n", str);
  17.  
    return 0;
  18.  
    }

編譯運行

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

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

  1.  
    #include <stdio.h>
  2.  
    #include <stdlib.h>
  3.  
    #include <string.h>
  4.  
    char *fun()
  5.  
    {
  6.  
    int i;
  7.  
    char *buffer = (char *)malloc(sizeof(char) * 20);
  8.  
    strcpy(buffer, "abcdefg");
  9.  
    return buffer;
  10.  
    }
  11.  
    int main()
  12.  
    {
  13.  
    char *str;
  14.  
    str = fun();
  15.  
    printf("%s\n", str);
  16.  
    return 0;
  17.  
    }


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

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

  1.  
    #include <stdio.h>
  2.  
    #include <stdlib.h>
  3.  
    #include <string.h>
  4.  
    void fun(char *str, int size)
  5.  
    {
  6.  
    char *s = "abcdefghijklmnopq";
  7.  
    strncpy(str, s, size);
  8.  
    }
  9.  
    int main()
  10.  
    {
  11.  
    int size = 10;
  12.  
    char *str = (char *)malloc(sizeof(char) * size);
  13.  
    fun(str, size);
  14.  
    printf("%s\n", str);
  15.  
    free(str);
  16.  
    return 0;
  17.  
    }

編譯運行


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM