變量屬性與存儲


主要學習變量的各種屬性,以及加深了變量與存儲的關係。真的不太熟悉的就是鏈接屬性吧。(存儲類名字不熟悉)


1.程序運行的基本概念:
1).存儲類:
存儲類就是存儲類型,描述變量在哪裏存放(堆,棧,代碼段(.text),數據段(.data),bss段(ZI段)...),在哪裏內存段中存儲。
2).作用域:
描述一個變量起作用的範圍,一般是是當前代碼塊/文件作用域(注意同名的全局變量和局部變量)
3).生命週期:
描述一個變量何時誕生(運行時分配空間給這個變量),何時消亡(運行時回收內存,以後不能訪問這個地址或這個地址與變量無關)
4).鏈接屬性:
源代碼在編譯成.o文件時,裏面有符號和代碼段、數據段、bss段等分段。符號是編程中的函數名,變量名;程序運行時能變量名,函數名能和內存聯繫起來,靠的就是符號
做鏈接。

鏈接時能把.o文件實際上就是把符號和相應的段鏈接起來。

C語言中的符號有三種鏈接屬性:外連接屬性、內鏈接屬性、無連接屬性。

2.linux下的內存映像:


1).代碼段,只讀數據段:
a.代碼段對應程序中的代碼(函數),在linux中又叫文本段(.text)
b.只讀數據段保存那些程序運行期間不能被寫的數據(部分平臺下的const)

2).數據段,清零段(bss):
a.數據段存:顯示初始化非零的全局變量;顯式初始化爲非0的static局部變量
b.bss段存:顯式初始化爲0或者未顯式初始化的全局變量;顯式初始化爲0或未顯式初始化的static局部變量

3).堆

4).文件映射區:
進程打開文件後,從硬盤把文件的內容讀到文件映射區,之後在內存中操作這個文件,事後把文件映射區的內容寫回硬盤。

5).棧:
存放局部變量,函數調用傳參也會用到棧;(棧髒,注意棧裏的變量要是沒初始化值是不定的)

6).內核映射區:
(1)內核映射區就是將操作系統內核程序映射到這個區域了。
    (2)利用虛擬技術,每個進程都認爲0xc0000000以上區域是內核空間,其以下是自己的活動空間。每個進程的獨享空間不同,但是內核空間是唯一的。

補充:
1.C語言程序運行時環境有一定要求,單獨個人寫的C語言程序沒法直接在內存中運行,需要外部一定的協助,這段協助的代碼叫加載運行代碼(或者叫構建C運行時環境的代碼,這一段代碼在操作系統下是別人寫好的,會自動添加到我們寫的程序上,這段代碼的主要作用是:給全局變量賦值、清bss段。),要是沒OS就要我們自己寫加載運行代碼咯。

2.數據段的全局變量或靜態局部變量都是有非0的初值的,這些初值在main函數運行之前就已經被初始化了,是重定位期間完成的初始化。



3.存儲類相關的關鍵字:
(1)auto:
只能修飾局部變量(自然定義在棧上),我們平時其實用的就是auto,不過是省略了;

(2)static:
1).修飾局部變量:靜態局部變量和普通局部變量只要是存儲類不同
2).修飾全局變量:靜態全局變量和非靜態全局變量主要是鏈接屬性不同。
靜態局部變量和全局變量異同:
同:存儲類和生命週期
異:作用域:靜態局部變量和非靜態局部變量一樣;
鏈接屬性:靜態局部變量是無連接,全局變量是外連接

(3)register
一般用來修飾全局變量,使其儘量放在寄存器中處理更快。(寄存器不夠用就放不進去了)

(4)extern(C編譯是以單個.c源文件爲單位)
extern主要用來聲明全局變量,使其可以跨文件使用。是在a.c中定義全局變量(int a=10;),而在b.c中使用該變量前需要聲明它(extern int a;)。
將來在鏈接的時候鏈接器會在別的.o文件中找到這個同名變量。

(5)volatile:(英文意思:易變的,可變的)
用來修飾變量,表示這個變量可以被編譯器以外的(編譯器沒有預料的情況,自然不是代碼造成的)東西改變。
譬如在中斷處理程序isr中更改了這個變量的值,譬如多線程中在別的線程更改了這個變量的值,譬如硬件自動更改了這個變量的值(一般這個變量是一個寄存器的值);
編譯器在一般情況下會優化,幫助提高效率;但在以上三種情況下編譯器優化是預料不到會產生什麼結結果,爲了避免出錯,用volatile告訴編譯器不用優化。
例子:
int a,b,c;
a=10;
b=a;
    c=b;  
正常情況下編譯器會優化,只取一次值,寫三次(三次都寫10)這時若因爲以上三種情況在2-3句a的值或者3-4句間b的值被改變,那麼結果就錯了。如果用
volatile int a,b,c;那麼不優化取三次寫三次就不會出錯了。

(6)restrict:
1)c99中才支持的,所以很多延續c89的編譯器是不支持restrict關鍵字,gcc支持的。
2)restrict只用來修飾指針,修飾普通變量就跟沒用一樣;restrict也是和編譯器行爲特徵有關的(加上用來優化),加上這個說明指針指向的變量值只能用指針來改變;
參考:http://blog.chinaunix.net/uid-22197900-id-359209.html

(7)typedef
。。。


4.屬性詳解:
(1).作用域:
1).
局部變量代碼塊作用域:

a.一個局部變量可以被訪問和使用的範圍僅限於定義這個局部變量的代碼塊中定義式之後的部分

b.全局變量和函數的文件作用域:
函數和全局變量的作用域是定義所在的整個.c文件之內定義式之後的部分;(兩個方法:在前面定義;在後面定義,前面聲明)
c.在c89標準的編譯器中(現在很多編譯器還延續使用c89標準),所有的局部變量必須先定義在最前面,在變量定義之前不能有一句執行代碼。在c99標準的編譯器中(gcc兼容c99標準)可以允許在代碼塊內任意地方定義變量。但是允許定義的變量還是隻能使用在定義了之後,定義之前還是不能用的。

2).同名變量掩蔽規則:
作用域不重疊,重名沒關係;重疊了,在交疊範圍內作用域小的那個會掩蓋掉作用域大的。

(2).生命週期:
棧變量:
堆變量;
數據段,bss段:定義的全局變量直至程序終結,期間一直佔着內存;局部static的生命週期和全局變量一樣,作用域和非靜態局部變量一樣;
代碼段,只讀段;不關心


(3).鏈接屬性:
1).編譯是爲了將函數/變量等變成.o二進制的機器碼格式,鏈接是爲了將各個獨立分開的二進制的函數鏈接起來形成一個整體的二進制可執行程序

2).編譯以文件爲單位、鏈接以工程爲單位
(1)編譯器工作時是將所有源文件依次讀進來,單個爲單位進行編譯的。
(2)鏈接的時候實際上是把第一步編譯生成個單個的.o文件整體的輸入,然後處理鏈接成一個可執行程序。
3).
外連接:外部鏈接屬性,也就是說這傢伙可以在整個程序範圍內(言下之意就是可以跨文件)進行鏈接,譬如普通的函數和全局變量屬於外連接。
內鏈接:(c文件內部)內部鏈接屬性,也就是說這傢伙可以在當前c文件內部範圍內進行鏈接(言下之意就是不能在當前c文件外面的其他c文件中進行訪問、鏈接)。static修飾的函數/全局變量屬於內鏈接。

無連接:這個符號本身不參與鏈接,它跟鏈接沒關係。所有的局部變量(auto的、static的)都是無連接的

4).static的第二種用法:修飾全局變量和函數(可以在一定程度上解決重名問題)
普通的(非靜態)的函數/全局變量,默認的鏈接屬性是外部的
static(靜態)的函數/全局變量,鏈接屬性是內部鏈接。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章