1. 程式人生 > >嵌入式C語言之深度解讀C語言的儲存域,作用域,生命週期,連結屬性

嵌入式C語言之深度解讀C語言的儲存域,作用域,生命週期,連結屬性


***儲存類:
   就是儲存型別,描述,C語言變數的儲存地址。
   記憶體的管理方式:棧  堆  資料段  bss段  .text段。
   一個變數的儲存型別就是描述這個變數儲存在何種記憶體段之中。
   

***作用域:
   描述變數起作用的範圍。
   函式名和全域性變數是檔案作用域,在整個.c資料夾之內都可以訪問這些東西。
   同名變數的掩蓋規則:區域性變數會掩蓋全域性變數(就近原則)
   

***生命週期:
   描述變數誕生(執行時分配記憶體空間)和死亡(執行完回收記憶體空間)的時間在
   區域性變數儲存在棧上,生命週期是臨時的,在程式碼執行的過程中按照需求去創造,使用,消亡。區域性變數在每一次被呼叫時都會重新被建立,使用,消亡。
   堆記憶體:堆記憶體空間客觀存在,由作業系統維護,我們程式只是去申請,使用完再釋放。其生命週期就是malloc申請~free消亡。
   資料段和bss段:全域性變數生命週期時永久的,在程式被執行時誕生,程式終止時消亡。他佔有的記憶體是不能被釋放的。所以不能申請太多的全域性變數。
   
   程式碼段和只讀資料段:其實就是函式,生命週期永久。


***連結屬性:
   編譯階段就是將原始碼變成.o目標檔案,目標檔案包括很多的符號,程式碼段,資料段,bss段。符號是程式設計中的變數名,函式名。執行時變數名,函式名能夠和對應的記憶體對應起來,就靠符號來做連結的。
   .     o的目標檔案連結生成最紅可執行檔案時,其實就是把無符號和對應的段給連結起來。C語言的符號有三種連結屬性:外連結屬性,內連結屬性,無連結屬性。
   
   C語言的組織架構:多個c檔案+多個h檔案。 
   程式的生成過程就是編譯+連結。編譯時為了將函式/變數變成.o二進位制機器碼,連結是為了將獨立分開的二進位制函式連結起來形成一個整體的二進位制可執行程式。
   編譯以檔案為單位,連結以工程為單位。
   
   外連結就是可以在整個程式內進行連結(可以跨檔案),例如函式和全域性變數。
   內部連結在當前c檔案內進行連結,不能跨檔案。例如static修飾的函式,全域性變數
   無連結就是符號本身不參與連結,例如所有的區域性變數
   
   
   static第二種用法:修飾區域性變數和函式
   普通的函式/全域性變數,預設連結屬性是外部的。
   static(靜態的)函式/全域性變數,連結屬性是內部連結。
   
***linux下C語言程式的映像:


   程式碼段,只讀資料段:對應著程式中的程式碼段,程式碼段在linux中叫文字段(.text)
   資料段,bss段:
   1.資料段存放顯示初始化為非0的全域性變數,顯示初始化為非0的static的區域性變數。
   2.bss段存放顯示初始化為0,或者未顯示初始化的全域性變數,顯示初始化為0或者未顯示初始化的static區域性變數。
   3.檔案對映區:程序開啟檔案之後,將這個檔案的內容從硬碟讀取到程序的檔案對映區,以後就直接在記憶體中操作這個檔案,讀取完了後在儲存時再將記憶體中的檔案寫到硬碟之中去。
   4.核心對映區:將作業系統的核心程式對映到這個區域。對於每一個linux程序來說,整個系統只有自己和核心而已。(運用了虛擬地址技術)

   
***儲存類相關關鍵字
   1.auto:僅僅只有一種作用:修飾區域性變數,表示這個區域性變數是自動區域性變數,分配在棧上面。 則如果不初始化則值是隨機的,生命週期是臨時的。auto區域性變數就是預設定義的普通區域性變數。
   
   **2.static用法:1).用來修飾區域性變數,形成靜態區域性變數。
   注:靜態區域性變數和非靜態區域性變數二者的區別是儲存類不同。非*分配在棧上面,靜態分配在bss段。
                 2).修飾全域性變數,形成靜態全域性變數。
   注:靜態全域性變數和非靜態全域性變數的區別:連結屬性不同。
   靜態區域性變數和全域性變數儲存類,生命週期相同,作用域和連結屬性不同。
   靜態區域性變數作用域是程式碼塊,連結屬性無連線;
   全域性變數的作用域是檔案作用域,和函式是一樣的,連結屬性是外連結。
   
   3.register:一個作用:一般全域性變數用其修飾。由其修飾的全域性變數編譯器會盡量將他分配在暫存器中。(一般分配在記憶體之中)分配在暫存器中使得讀寫速度加快。register多用來修飾被反覆高頻率使用的變數,可以通過改善這個變數的訪問效率極大提高程式的執行速度。
   
   **4.extern:宣告全域性變數,目的主要是在a.c中定義變數,在b.c使用變數。 在a.c使用***前先用extern 宣告***,並且它的原型和宣告相同。連結時聯結器會在別的.O檔案之中找到這個同名變數。(前提是兩個.c檔案必須在同一個資料夾內)
   
   **5.volatile:修飾一個可以被編譯器之外的東西改變的變數。(編譯器之內意思是變數值的改變是程式碼的作用,編譯器之外意思是變數值的改變不是當前程式碼造成的,編譯器在編譯當前程式碼時無法預知。例如中斷更改引用的變數,多執行緒更改公用的變數,以及暫存器直接更改變數值等)編譯器在遇到volatile修飾的變數時,不會對其進行優化。  
   
   6.restrict:和編譯器的優化處理有關的,只用來修飾指標,不能修飾普通變數。
   
***總結:
   1.普通的區域性變數自動分配在棧上,作用域是程式碼塊區域,生命週期是臨時的,連結屬性為無連結。定義時如果未初始化則值是隨機分配的,變數的地址由執行時棧上分配得到的,多次執行時地址不一定相同,函式不能返回該變數的地址(指標)作為返回值。
   
   2.靜態區域性變數分配在資料段/bss段(顯示初始化為非0則在資料段,為0或者未顯示初始化則在bss段,)作用域是程式碼塊作用區域,生命週期永久,連結屬性為無連結。定義時如果未初始化則值為0,變數的地址由執行時環境在載入程式時環境在載入地址時確定,整個程式執行時唯一不變;靜態區域性變數其實就是作用域為程式碼塊區域的區域性變數。驚天區域性變數可以用全域性變數實現。(程式避免太多的全域性變數,破壞程式結構性)
   
   3.靜態全域性變數/靜態函式和普通全域性變數/普通函式的唯一差別是:static使用全域性變數/函式的連結屬性由外部連結轉為內部變數。
   
   注:定義的同時初試化則一定會被認為是定義;定義時未初始化則會被認為是定義或者宣告;
       使用extern被認為是宣告,(實際上也有定義,實際上就是宣告這個變數為外部連結)