易錯、經典問題:return不可返回指向棧內存的指針

預備知識:內存的分類

C/C++程序佔用的內存分爲兩大類:靜態存儲區動態存儲區。其示意圖以下所示:git

數據保存在靜態存儲區與動態存儲區的區別就是:靜態存儲區在編譯-連接階段已經肯定了,程序運行過程當中不會變化,只有當程序退出的時候,靜態存儲區的內存纔會被系統回收。動態存儲區是在程序運行過程當中動態分配的。github

在其它地方咱們還能夠看到內存分配還有其餘分類,那些都是細分的分類,好比文字常量區、全局數據區等,都歸爲靜態存儲區這一個大類。數組

關於內存的分類這裏只是大體說明一下,關於內存更詳細的內容可查看往期筆記:微信

【C語言筆記】內存筆記函數

例子:return返回指向棧內存指針

先看一個return返回指向棧內存指針的例子:指針

#include <stdio.h>

char *GetStr(void)
{
    char p[] = "Hello"; /* 保存在棧中 */
    return p;
}

int main(void) 
{
    char *str = NULL;
    str = GetStr();
    printf("%s\n", str);
    return 0;
}

程序編譯、運行的結果以下:code

能夠看到,編譯出現警告:blog

warning: function returns address of local variable內存

運行結果並非咱們指望的輸出字符串Hello字符串

那是由於GetStr函數返回指向棧內存的指針,這裏的變量p是局部變量,而局部變量是分配在棧上的。即Hello保存在棧內存上,棧內存在函數調用結束時會自動銷燬,所以此時的p裏的內容是未知的,因此結果無輸出。

下面咱們把GetStr函數修改成:

char *GetStr(void)
{
    char *p = "Hello";  /* p在棧上,Hello在靜態區(常量區) */
    return p;
}

此時編譯運行的結果是怎樣的呢?結果爲:

能夠看到能正常輸出。爲何這裏又能夠正常輸出呢?由於這裏的p雖然分配在棧上,可是此時的Hello是一個字符串常量,其存儲在靜態存儲區。在調用GetStr函數結束時其也不會被銷燬。

這裏可能有些人會有疑惑,一樣是Hello,爲何一個在棧上,一個在靜態區。

char *p = "Hello";

此處首先定義了一個指針變量p,編譯器就會爲指針變量開闢了棧空間。而此時並無空間來存放Hello,因此Hello只能存儲在靜態區。

char p[] = "Hello";

此處首先定義一個數組p,由於未給出數組大小,因此此時數組大小未肯定。而後把Hello保存在這個數組裏,編譯器就會爲數組p開閉適當的棧空間來存儲Hello

相關筆記:【C語言筆記】char *str與char str[]的區別

其它替代方法

從上面的例子咱們知道,若函數返回指向棧內存的指針,所獲得的結果並非咱們想要的。除了上面的方法以外,這裏還有以下幾種解決方法:

一、把p定義爲全局變量,由於全局變量存儲在靜態存儲區,程序結束纔會釋放。可是這樣會致使函數是不可重入的。關於函數的重入與不可重入可查看往期筆記。

二、在GetStr函數中使用malloc申請動態內存,但使用完必定要記得使用free進行釋放,不然會致使內存泄漏。示例代碼以下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *GetStr(void)
{
    char *p = (char*)malloc(64*sizeof(char));
    strcpy(p, "Hello");
    return p;
}

int main(void) 
{
    char *str = NULL;
    str = GetStr();
    printf("%s\n", str);
    free(str);  /* 釋放str指向的堆內存 */
    return 0;
}

三、能夠將變量p聲明爲static靜態變量。但這也會致使函數是不可重入的。示例代碼以下:

char *GetStr(void)
{
    static char p[] = "Hello";
    return p;
}

以上就是本次筆記分享的內容,若有錯誤,歡迎指出!


個人我的博客:https://zhengnianli.github.io/ 個人微信公衆號:嵌入式大雜燴

相關文章
相關標籤/搜索