存儲類、做用域、生命週期、連接屬性

1.linux下C語言程序的內存映像代碼段(.text)、數據段(.data)、bss段、棧、堆的概念linux

代碼段(.text)

(1)對應着程序中的代碼(函數),代碼段在linux中又叫文本段(.text)
(2)部分平臺下的const修飾的變量。編程

數據段(.data)

一、顯式初始化爲非0的全局變量;架構

二、顯式初始化爲非0的static局部變量函數

bss段

一、顯式初始化爲0或者未顯式初始化的全局變量;優化

二、顯式初始化爲0或未顯式初始化的static局部變量。spa

局部變量分配在棧上;函數調用傳參過程也會用到棧
用時須要手工申請。
文件映射區 文件映射區就是進程打開了文件後,將這個文件的內容從硬盤讀到進程的文件映射區,之後就直接在內存中操做這個文件,讀寫完了後在保存時再將內存中的文件寫到硬盤中去。
內核映射區

(1)內核映射區就是將操做系統內核程序映射到這個區域了。
(2)對於linux中的每個進程來講,它都覺得整個系統中只有它本身和內核而已。它認爲內存地址0xC0000000如下都是它本身的活動空間,0xC0000000以上是OS內核的活動空間。
(3)每個進程都活在本身獨立的進程空間中,0-3G的空間每個進程是不一樣的(由於用了虛擬地址技術),可是內核是惟一的。操作系統

 

2.存儲類相關的關鍵字
(1)講述存儲類關鍵字auto、static、register,其中重點是static。指針

  auto關鍵字在C語言中只有一個做用,那就是修飾局部變量。默認就是autorest

  static兩種用法:生命週期

  第一種修飾局部變量爲靜態局部變量。

  一、靜態局部變量在存儲類方面和全局變量同樣。(數據段或bss段)
  二、靜態局部變量在生命週期方面和全局變量同樣。(整個程序)
  三、靜態局部變量和全局變量的區別是:做用域、鏈接屬性。靜態局部變量做用域是代碼塊做用域(和普通局部變量是同樣的)、連接屬性是無鏈接;全局變量做用域是文件做用域(和函數是同樣的)、連接屬性方面是外鏈接。

  第二種是修飾全局變量,函數名。表示只在當前文件下有用,用於避免重名。(修飾後的連接從外鏈接變成內連接)

  register:用register修飾的變量編譯器優先分配到寄存器中。


(2)講述存儲類關鍵字extern、volatile、restrict、typedef,其中重點是extern和volatile。

  extern:在聲明中用,表示這個變量在其餘文件中定義。

  volatile:比較敏感,可能被外界環境改變的量。用這個修飾後能夠避免編譯器過分優化。

  typedef:

3.做用域

  一、局部變量的代碼塊做用域

  (1)代碼塊基本能夠理解爲一對大括號{}括起來的部分。
  (2)代碼塊不等於函數,由於if while for都有{}。因此代碼塊<=函數
  (3)局部變量的做用域是代碼塊做用域,也就是說一個局部變量能夠被訪問和使用的範圍僅限於定義這個局部變量的代碼塊中定義式以後的部分。

  二、函數名和全局變量的文件做用域
  (1)文件做用域的意思就是全局的訪問權限,也就是說整個.c文件中均可以訪問這些東西。這就是平時所說的局部和全局,全局就是文件做用域。
  (2)詳細準確的說:函數和全局變量的做用域是定義所在的整個.c文件以內定義式以後的部分。

  三、同名變量的掩蔽規則
  (1)問題:編程時,不可避免會出現同名變量。變量同名後不必定會出錯。
  (2)首先,若是兩個同名變量做用域不一樣且沒有交疊,這種狀況下同名沒有任何影響。
  (3)其次,若是兩個同名變量做用域有交疊,C語言規定在做用域交疊範圍內,做用域小的一個變量會掩蔽掉做用域大的那個(縣官不如現管)。

4.生命週期 

  棧變量的生命週期
  (1)局部變量(棧變量)存儲在棧上,生命週期是臨時的。臨時的意思就是說:代碼執行過程當中按照須要去建立、使用、消亡的。
  (2)譬如一個函數內定義的局部變量,在這個函數每一次被調用時都會建立一次,而後使用,最後在函數返回的時候消亡。

  堆變量的生命週期
  (1)首先要明白:堆內存空間是客觀存在的,是由操做系統維護的。咱們程序只是去申請而後使用而後釋放。
  (2)咱們只關心咱們程序使用堆內存的這一段時間,所以堆變量也有了本身的生命週期,就是:從malloc申請時誕生,而後使用,直到free時消亡。

  數據段、bss段變量的生命週期
  (1)全局變量的生命週期是永久的。永久的意思就是在程序被執行時誕生,在程序終止時消亡。
  (2)全局變量所佔用的內存是不能被程序本身釋放的,因此程序若是申請了過多的全局變量會致使這個程序一直佔用大量內存。
  (3)若是說堆內存是圖書館借的書,那麼全局變量就是本身買的書。

  代碼段、只讀段的生命週期
  (1)其實就是程序執行的代碼,其實就是函數,它的生命週期是永久的。不過通常代碼的生命週期咱們並不關注。
  (2)有時候放在代碼段的不僅是代碼,還有const類型的常量,還有字符串常量。(const類型的常量、字符串常量有時候放在rodata段,有時候放在代碼段,取決於平臺)

5.連接屬性  

  一、C語言程序的組織架構:多個C文件+多個h文件
  (1)龐大、完整的一個C語言程序(譬如linux內核、uboot)由多個c文件和多個h文件組成的。
  (2)程序的生成過程就是:編譯+連接。編譯是爲了將函數/變量等變成.o二進制的機器碼格式,連接是爲了將各個獨立分開的二進制的函數連接起來造成一個總體的二進制可執行程序。
  二、編譯以文件爲單位、連接以工程爲單位
  (1)編譯器工做時是將全部源文件依次讀進來,單個爲單位進行編譯的。
  (2)連接的時候其實是把第一步編譯生成個單個的.o文件總體的輸入,而後處理連接成一個可執行程序。
  三、三種連接屬性:外鏈接、內連接、無連接
  (1)外鏈接的意思就是外部連接屬性,也就是說這傢伙能夠在整個程序範圍內(言下之意就是能夠跨文件)進行連接,譬如普通的函數和全局變量屬於外鏈接。
  (2)內連接的意思就是(c文件內部)內部連接屬性,也就是說這傢伙能夠在當前c文件內部範圍內進行連接(言下之意就是不能在當前c文件外面的其餘c文件中進行訪問、連接)。static修飾的函數/全局變量屬於內連接。
  (3)無鏈接的意思就是這個符號自己不參與連接,它跟連接不要緊。全部的局部變量(auto的、static的)都是無鏈接的
  四、函數和全局變量的同名衝突(static修飾)
  (1)由於函數和全局變量是外部連接屬性,就是說每個函數和全局變量未來在整個程序中全部的c文件都能被訪問,所以在一個程序中的全部c文件中不能出現同名的函數/同名的全局變量。
  (2)最簡單的解決方案就是起名字不要重複,可是很難作到。主要緣由是一個很大的工程中函數和全局變量名字太多了,並且一個大工程不是一我的完成的,是不少人協做完成,因此很難保證不會重名。解決方案呢?
  (3)現代高級語言中完美解決這個問題的方法是命名空間namespace(其實就是給一個變量帶上各個級別的前綴)。可是C語言不是這麼解決的。
  (4)C語言比較早碰到這個問題,當時還沒發明namespace概念,當時C語言就發明了一種不是很完美可是湊活能用的解決方案,就是三種連接屬性的方法。
  (5)C語言的連接屬性解決重名問題思路是這樣的:咱們將明顯不會在其餘c文件中引用(只在當前c文件中引用)的函數/全局變量,使用static修飾使其成爲內連接屬性,這樣在未來鏈接時即便2個c文件中有重名的函數/全局變量,只要其中一個或2個爲內連接屬性就沒事。
  (6)這種解決方案在必定程度上解決了問題。可是沒有從根本上解決問題,留下了不少麻煩。因此這個就致使了C語言寫很大型的項目難度很大。

  五、static的第二種用法:修飾全局變量和函數
  (1)普通的(非靜態)的函數/全局變量,默認的連接屬性是外部的
  (2)static(靜態)的函數/全局變量,連接屬性是內部連接。

最後的總結

(1)普通(自動)局部變量分配在棧上,做用域爲代碼塊做用域,生命週期是臨時,鏈接屬性爲無鏈接。定義時若是未顯式初始化則其值隨機,變量地址由運行時在棧上分配獲得,屢次執行時地址不必定相同,函數不能返回該類變量的地址(指針)做爲返回值。(2)靜態局部變量分配在數據段/bss段(顯式初始化爲非0則在數據段,顯式初始化爲0或未顯示初始化則在bss段),做用域爲代碼塊做用域(人爲規定的),生命週期爲永久(自然的),連接屬性爲無鏈接(自然的)。定義時若是未顯式初始化則其值爲0(自然的),變量地址由運行時環境在加載程序時肯定,整個程序運行過程當中惟一不變;靜態局部變量其實就是做用域爲代碼塊做用域(同時連接屬性爲無鏈接)的全局變量。靜態局部變量能夠改成用全局變量實現(程序中儘可能避免用全局變量,由於會破壞結構性)。(3)靜態全局變量/靜態函數和普通全局變量/普通函數的惟一差異是:static使全局變量/函數的連接屬性由外部連接(整個程序全部文件範圍)轉爲內部連接(當前c文件內)。這是爲了解決全局變量/函數的重名問題(C語言沒有命名空間namespace的概念,所以在程序中文件變多以後全局變量/函數的重名問題很是嚴重,將沒必要要被其餘文件引用的全局變量/函數聲明爲static能夠很大程度上改善重名問題,可是仍未完全解決)。(4)寫程序儘可能避免使用全局變量,尤爲是非static類型的全局變量。能肯定不會被其餘文件引用的全局變量必定要static修飾。(5)注意區分全局變量的定義和聲明。通常規律以下:若是定義的同時有初始化則必定會被認爲是定義;若是隻是定義而沒有初始化則有可能被編譯器認爲是定義,也可能被認爲是聲明,要具體分析;若是使用extern則確定會被認爲是聲明(實際上使用extern也能夠有定義,實際上加extern就是明確聲明這個變量爲外部連接屬性)。(6)全局變量應該定義在c文件中而且在頭文件中聲明,而不要定義在頭文件中(由於若是定義在頭文件中,則該頭文件被多個c文件包含時該全局變量會重複定義)。(7)在b.c中引用a.c中定義的全局變量/函數有2種方法:一是在a.h中聲明該函數/全局變量,而後在b.c中#include <a.h>;二是在b.c中使用extern顯式聲明要引用的函數/全局變量。其中第一種方法比較正式。(8)存儲類決定生命週期,做用域決定連接屬性(9)宏和inline函數的連接屬性爲無鏈接。

相關文章
相關標籤/搜索