C語言函數退出後,局部變量就被釋放了,為何會有這一過程?

1 個回答
嵌入式时代
2019-06-11

這個問題我正好整理過:

之前我的文章裡討論過C語言中的全局變量和局部變量,並且介紹了局部變量在函數返回後,就會被釋放。而全局變量卻可以一直保存到程序結束,這是為什麼呢?

在回答這個問題之前,請看下面這張非常經典的圖:這個圖就是程序在運行所需的內存佈局。簡單來說,就是程序在運行時會佔用內存,佔用的內存每個區域用途都是不同的,有的區域用做堆區,有的用做棧區,等等。

這個問題我正好整理過:

之前我的文章裡討論過C語言中的全局變量和局部變量,並且介紹了局部變量在函數返回後,就會被釋放。而全局變量卻可以一直保存到程序結束,這是為什麼呢?

在回答這個問題之前,請看下面這張非常經典的圖:這個圖就是程序在運行所需的內存佈局。簡單來說,就是程序在運行時會佔用內存,佔用的內存每個區域用途都是不同的,有的區域用做堆區,有的用做棧區,等等。

為什麼函數返回時,局部變量就不能用了

程序每調用一個函數,系統就自動在棧區劃分一塊區域給該函數使用,函數內部定義的局部變量,也存在此處。因為並不能知道系統分配的棧區原來填充的是什麼樣的數據,所以如果函數內部定義的局部變量沒有初始化(沒有賦初值)就使用它,它的值也是未知的。

這個問題我正好整理過:

之前我的文章裡討論過C語言中的全局變量和局部變量,並且介紹了局部變量在函數返回後,就會被釋放。而全局變量卻可以一直保存到程序結束,這是為什麼呢?

在回答這個問題之前,請看下面這張非常經典的圖:這個圖就是程序在運行所需的內存佈局。簡單來說,就是程序在運行時會佔用內存,佔用的內存每個區域用途都是不同的,有的區域用做堆區,有的用做棧區,等等。

為什麼函數返回時,局部變量就不能用了

程序每調用一個函數,系統就自動在棧區劃分一塊區域給該函數使用,函數內部定義的局部變量,也存在此處。因為並不能知道系統分配的棧區原來填充的是什麼樣的數據,所以如果函數內部定義的局部變量沒有初始化(沒有賦初值)就使用它,它的值也是未知的。

當函數執行完畢,返回時,系統將收回這塊分配的棧區,所以函數的局部變量的值就不能繼續使用了。

說了這麼多空的,我們來看一個例子,下面的代碼非常簡單,就是在 test 函數中定一個了一個局部 int 型變量 i,然後打印出它的值,再賦值為 321,然後在 main 函數中調用 它兩次。

這個問題我正好整理過:

之前我的文章裡討論過C語言中的全局變量和局部變量,並且介紹了局部變量在函數返回後,就會被釋放。而全局變量卻可以一直保存到程序結束,這是為什麼呢?

在回答這個問題之前,請看下面這張非常經典的圖:這個圖就是程序在運行所需的內存佈局。簡單來說,就是程序在運行時會佔用內存,佔用的內存每個區域用途都是不同的,有的區域用做堆區,有的用做棧區,等等。

為什麼函數返回時,局部變量就不能用了

程序每調用一個函數,系統就自動在棧區劃分一塊區域給該函數使用,函數內部定義的局部變量,也存在此處。因為並不能知道系統分配的棧區原來填充的是什麼樣的數據,所以如果函數內部定義的局部變量沒有初始化(沒有賦初值)就使用它,它的值也是未知的。

當函數執行完畢,返回時,系統將收回這塊分配的棧區,所以函數的局部變量的值就不能繼續使用了。

說了這麼多空的,我們來看一個例子,下面的代碼非常簡單,就是在 test 函數中定一個了一個局部 int 型變量 i,然後打印出它的值,再賦值為 321,然後在 main 函數中調用 它兩次。

在編譯執行前,根據我們的理論,局部變量如果沒有初始化,那它的值是未知的,因此第一個 test 調用,會打印出隨機的一個數。雖然第一個 test 函數在最後對局部變量 i 賦值了 321,但是在它返回後,就被釋放了。因此第二次調用 test 時,輸出應該還是一個隨機數。那對不對呢?編譯執行,發現程序輸出

134567128
321

出乎意料,第二次打印出的值正是第一個 test 末尾賦的值 321。有一種初學者是這樣,原本就沒有把這條語法規則記牢,或者對自己的記憶力沒信心,看到這個結果就會想:哦那肯定是我記錯了,改過來記吧,應該是“函數中的局部變量具有一直存在的固定的存儲空間,每次函數調用時使用它,返回時也不釋放,再次調用函數時它應該還能保持上次的值”。

這個問題我正好整理過:

之前我的文章裡討論過C語言中的全局變量和局部變量,並且介紹了局部變量在函數返回後,就會被釋放。而全局變量卻可以一直保存到程序結束,這是為什麼呢?

在回答這個問題之前,請看下面這張非常經典的圖:這個圖就是程序在運行所需的內存佈局。簡單來說,就是程序在運行時會佔用內存,佔用的內存每個區域用途都是不同的,有的區域用做堆區,有的用做棧區,等等。

為什麼函數返回時,局部變量就不能用了

程序每調用一個函數,系統就自動在棧區劃分一塊區域給該函數使用,函數內部定義的局部變量,也存在此處。因為並不能知道系統分配的棧區原來填充的是什麼樣的數據,所以如果函數內部定義的局部變量沒有初始化(沒有賦初值)就使用它,它的值也是未知的。

當函數執行完畢,返回時,系統將收回這塊分配的棧區,所以函數的局部變量的值就不能繼續使用了。

說了這麼多空的,我們來看一個例子,下面的代碼非常簡單,就是在 test 函數中定一個了一個局部 int 型變量 i,然後打印出它的值,再賦值為 321,然後在 main 函數中調用 它兩次。

在編譯執行前,根據我們的理論,局部變量如果沒有初始化,那它的值是未知的,因此第一個 test 調用,會打印出隨機的一個數。雖然第一個 test 函數在最後對局部變量 i 賦值了 321,但是在它返回後,就被釋放了。因此第二次調用 test 時,輸出應該還是一個隨機數。那對不對呢?編譯執行,發現程序輸出

134567128
321

出乎意料,第二次打印出的值正是第一個 test 末尾賦的值 321。有一種初學者是這樣,原本就沒有把這條語法規則記牢,或者對自己的記憶力沒信心,看到這個結果就會想:哦那肯定是我記錯了,改過來記吧,應該是“函數中的局部變量具有一直存在的固定的存儲空間,每次函數調用時使用它,返回時也不釋放,再次調用函數時它應該還能保持上次的值”。

還有一種初學者是懷疑論者或不可知論者,看到這個結果就會想:教材上明明說“局部變量的存儲空間在每次函數調用時分配,在函數返回時釋放”,那一定是教材寫錯了,教材也是人寫的,是人寫的就難免出錯,哦,連C99也這麼寫的啊,C99也是人寫的,也難免出錯,或者C99也許沒錯,但是反正運行結果就是錯了,計算機這東西真靠不住,太容易受電磁干擾和宇宙射線影響了,我的程序寫得再正確也有可能被幹擾得不能正確運行。

這兩種想法當然不對。我們說,因為並不能知道系統分配的棧區原來填充的是什麼樣的數據,所以如果函數內部定義的局部變量沒有初始化(沒有賦初值)就使用它,這個未知的值可以是隨機的,那既然是隨機值,它為什麼不可以恰好是 321 呢?

函數返回後,系統收回原分配的棧區,卻不一定會去清零它。如果下一次,恰好又把這塊棧區分配給 test 函數,局部變量 i 所在的區域恰好是上一次 i 所在的區域,而上次 test 函數返回之前,把這塊區域賦值為 321 了,那這次 i 的“未知值”就正好是 321 也就不奇怪了,對吧。

為了驗證我們上面的解答,我們在兩個 test 之間加一行 printf,再做次實驗:

這個問題我正好整理過:

之前我的文章裡討論過C語言中的全局變量和局部變量,並且介紹了局部變量在函數返回後,就會被釋放。而全局變量卻可以一直保存到程序結束,這是為什麼呢?

在回答這個問題之前,請看下面這張非常經典的圖:這個圖就是程序在運行所需的內存佈局。簡單來說,就是程序在運行時會佔用內存,佔用的內存每個區域用途都是不同的,有的區域用做堆區,有的用做棧區,等等。

為什麼函數返回時,局部變量就不能用了

程序每調用一個函數,系統就自動在棧區劃分一塊區域給該函數使用,函數內部定義的局部變量,也存在此處。因為並不能知道系統分配的棧區原來填充的是什麼樣的數據,所以如果函數內部定義的局部變量沒有初始化(沒有賦初值)就使用它,它的值也是未知的。

當函數執行完畢,返回時,系統將收回這塊分配的棧區,所以函數的局部變量的值就不能繼續使用了。

說了這麼多空的,我們來看一個例子,下面的代碼非常簡單,就是在 test 函數中定一個了一個局部 int 型變量 i,然後打印出它的值,再賦值為 321,然後在 main 函數中調用 它兩次。

在編譯執行前,根據我們的理論,局部變量如果沒有初始化,那它的值是未知的,因此第一個 test 調用,會打印出隨機的一個數。雖然第一個 test 函數在最後對局部變量 i 賦值了 321,但是在它返回後,就被釋放了。因此第二次調用 test 時,輸出應該還是一個隨機數。那對不對呢?編譯執行,發現程序輸出

134567128
321

出乎意料,第二次打印出的值正是第一個 test 末尾賦的值 321。有一種初學者是這樣,原本就沒有把這條語法規則記牢,或者對自己的記憶力沒信心,看到這個結果就會想:哦那肯定是我記錯了,改過來記吧,應該是“函數中的局部變量具有一直存在的固定的存儲空間,每次函數調用時使用它,返回時也不釋放,再次調用函數時它應該還能保持上次的值”。

還有一種初學者是懷疑論者或不可知論者,看到這個結果就會想:教材上明明說“局部變量的存儲空間在每次函數調用時分配,在函數返回時釋放”,那一定是教材寫錯了,教材也是人寫的,是人寫的就難免出錯,哦,連C99也這麼寫的啊,C99也是人寫的,也難免出錯,或者C99也許沒錯,但是反正運行結果就是錯了,計算機這東西真靠不住,太容易受電磁干擾和宇宙射線影響了,我的程序寫得再正確也有可能被幹擾得不能正確運行。

這兩種想法當然不對。我們說,因為並不能知道系統分配的棧區原來填充的是什麼樣的數據,所以如果函數內部定義的局部變量沒有初始化(沒有賦初值)就使用它,這個未知的值可以是隨機的,那既然是隨機值,它為什麼不可以恰好是 321 呢?

函數返回後,系統收回原分配的棧區,卻不一定會去清零它。如果下一次,恰好又把這塊棧區分配給 test 函數,局部變量 i 所在的區域恰好是上一次 i 所在的區域,而上次 test 函數返回之前,把這塊區域賦值為 321 了,那這次 i 的“未知值”就正好是 321 也就不奇怪了,對吧。

為了驗證我們上面的解答,我們在兩個 test 之間加一行 printf,再做次實驗:

再編譯執行,發現輸出為

134567128

看到沒,第一個 test 函數末尾賦的值,並不能保證一定傳給下一次 test。

未初始化的局部變量的值是不確定的,上一次誰使用過這一塊的棧區,恰好留在局部變量所在區域的值等於幾,這次局部變量的未初始化的默認值就是幾。

全局變量為什麼可以一直保留到程序結束?

這是因為,全局變量要麼保存在 bss 段,要麼保存在 data 段,這兩個段會一直保留到程序結束。

未初始化的全局變量保存在 bss 段,已初始化的全局變量保存在 data 段。

這個問題我正好整理過:

之前我的文章裡討論過C語言中的全局變量和局部變量,並且介紹了局部變量在函數返回後,就會被釋放。而全局變量卻可以一直保存到程序結束,這是為什麼呢?

在回答這個問題之前,請看下面這張非常經典的圖:這個圖就是程序在運行所需的內存佈局。簡單來說,就是程序在運行時會佔用內存,佔用的內存每個區域用途都是不同的,有的區域用做堆區,有的用做棧區,等等。

為什麼函數返回時,局部變量就不能用了

程序每調用一個函數,系統就自動在棧區劃分一塊區域給該函數使用,函數內部定義的局部變量,也存在此處。因為並不能知道系統分配的棧區原來填充的是什麼樣的數據,所以如果函數內部定義的局部變量沒有初始化(沒有賦初值)就使用它,它的值也是未知的。

當函數執行完畢,返回時,系統將收回這塊分配的棧區,所以函數的局部變量的值就不能繼續使用了。

說了這麼多空的,我們來看一個例子,下面的代碼非常簡單,就是在 test 函數中定一個了一個局部 int 型變量 i,然後打印出它的值,再賦值為 321,然後在 main 函數中調用 它兩次。

在編譯執行前,根據我們的理論,局部變量如果沒有初始化,那它的值是未知的,因此第一個 test 調用,會打印出隨機的一個數。雖然第一個 test 函數在最後對局部變量 i 賦值了 321,但是在它返回後,就被釋放了。因此第二次調用 test 時,輸出應該還是一個隨機數。那對不對呢?編譯執行,發現程序輸出

134567128
321

出乎意料,第二次打印出的值正是第一個 test 末尾賦的值 321。有一種初學者是這樣,原本就沒有把這條語法規則記牢,或者對自己的記憶力沒信心,看到這個結果就會想:哦那肯定是我記錯了,改過來記吧,應該是“函數中的局部變量具有一直存在的固定的存儲空間,每次函數調用時使用它,返回時也不釋放,再次調用函數時它應該還能保持上次的值”。

還有一種初學者是懷疑論者或不可知論者,看到這個結果就會想:教材上明明說“局部變量的存儲空間在每次函數調用時分配,在函數返回時釋放”,那一定是教材寫錯了,教材也是人寫的,是人寫的就難免出錯,哦,連C99也這麼寫的啊,C99也是人寫的,也難免出錯,或者C99也許沒錯,但是反正運行結果就是錯了,計算機這東西真靠不住,太容易受電磁干擾和宇宙射線影響了,我的程序寫得再正確也有可能被幹擾得不能正確運行。

這兩種想法當然不對。我們說,因為並不能知道系統分配的棧區原來填充的是什麼樣的數據,所以如果函數內部定義的局部變量沒有初始化(沒有賦初值)就使用它,這個未知的值可以是隨機的,那既然是隨機值,它為什麼不可以恰好是 321 呢?

函數返回後,系統收回原分配的棧區,卻不一定會去清零它。如果下一次,恰好又把這塊棧區分配給 test 函數,局部變量 i 所在的區域恰好是上一次 i 所在的區域,而上次 test 函數返回之前,把這塊區域賦值為 321 了,那這次 i 的“未知值”就正好是 321 也就不奇怪了,對吧。

為了驗證我們上面的解答,我們在兩個 test 之間加一行 printf,再做次實驗:

再編譯執行,發現輸出為

134567128

看到沒,第一個 test 函數末尾賦的值,並不能保證一定傳給下一次 test。

未初始化的局部變量的值是不確定的,上一次誰使用過這一塊的棧區,恰好留在局部變量所在區域的值等於幾,這次局部變量的未初始化的默認值就是幾。

全局變量為什麼可以一直保留到程序結束?

這是因為,全局變量要麼保存在 bss 段,要麼保存在 data 段,這兩個段會一直保留到程序結束。

未初始化的全局變量保存在 bss 段,已初始化的全局變量保存在 data 段。

歡迎在評論區一起討論,質疑。文章都是手打原創,每天最淺顯的介紹C語言、linux等嵌入式開發,喜歡我的文章就關注一波吧,可以看到最新更新和之前的文章哦。

相關推薦

推薦中...