顶级c程序员之路 基础篇 - 第一章 关键字的深度理解 number-1
?
c語言有32個關鍵字,每個關鍵字你都理解嗎?
今天出場的是:
?? ?auto , ?register, ?static, ? extern
為什么他們會一起呢,說到這里不得不談到c語言對變量的描述。
c給每個變量3個基本屬性:
1.????作用域?
2.????生命期
3.????存儲類型
?
一些基本概念定義:
塊的定義:
?? ? 在c語言里 : 是以{}定義的 ?很顯然函數是其中一個。
聲明和定義:
?? ? ?1 ?聲明是說有這個東西,但并不是馬上要產生出來,比如你跟別人說你會玩dota,但你現在并沒有玩。理解這個就好理解定義了。
?? ? ? 注意: ?聲明可以在多個地方, 而這些地方就是所謂的命名空間(c語言沒有這個概念,只是比喻),如果要引用全局變量,而又不在全局變量的作用范圍內,可以用extern關鍵字,表示我要引用這個變量,他已經在某個文件里或其他全局區域定義了,還有就是沒有定義,我也可以用,但link時候如果沒有定義就會報錯,無法解析(這個問題要牽扯到編譯器的鏈接原理,在以后會有分析)。
?? ? ?2 ? 定義就是產生它,一般我們都是聲明和定義一起的,全局變量都是定義在文件的最上面,所以沒有將聲明和定義分開,分開的情況是在,多個文件編譯中,如果其中一個(如果是頭文件)文件定義了一個全局變量a,那么你引用這個頭文件時(所謂的頭文件包含,就是把頭文件的東西copy到你的.c文件上面,只是方便瀏覽源代碼才有頭文件概念,一次定義,可以多處使用),很顯然,你的.c文件再定義一個就是重復定義了。還有一種情況是: 在一個.c文件里定義了一個全局變量int a,如果你又在其他.c文件里定義一個全局變量int a,link 時( 鏈接)就會出錯,如果是同名但不通類型,鏈接正常(編譯器可以區分他們,這個問題要牽扯到編譯器的鏈接原理,在以后會有分析)。
?? ? 注意: 定義只能定義一次,不能多次,如果要問為什么,那是因為定義就是給變量分配內存空間(按變量的類型分配大小),所以只能定義一次。
?
作用域: ?一個變量,就像當官一樣,也有自己的領地,比如全局變量(就是從定義地方到文件結束),但是在某個塊的內部,如果也定義了一個同名的變量(只可以定義一個),那這個地方就歸他管了,你沒有權利,他就是這個地方的皇帝,然而如果在其他塊中沒有與你同名的變量,那這些地方就由你管。看下面的圖知道, 如果不是在函數內定義的變量,都是全局變量,其實全局變量區,就是各個函數之間的區域。全局是向下擴展的,并不是整個文件,就是說,在某個定義全局變量的上面,已經沒有他的存在。如果上面的要訪問他,可以用extern關鍵字(聲明),這些對于局部變量行不通,對于局部變量在c語言里都必須定義在最前面。
要理解生命期先知道下面的。
?
看一下源代碼文件的結構:
?? ? ? ? ? ? ? 圖 1
上面這個圖配合下面概念講的。
全局變量和局部變量, 你真的理解他們嗎?其實談到這個,又得講一下c語言內存分配:
系統內核為高2GB,用戶為低2GB(在32位的內存中)
?
??????????????????????? 圖 2
BSS:?是“Block Started by Symbol”的縮寫,意為“以符號開始的塊”,在程序開始之前,內核將此段初始化為0。既這片內存在系統啟動之后本身初值為0。
在c語言里 內存可分為靜態存儲區和動態存儲區。
靜態存儲區: 就是程序在編譯時就已經分配了,比如,全局變量(全局靜態變量),靜態局部變量等,且初始化是在程序啟動開始時就已經初始化好了,對于這點,舉個例:
void Fun()
{
?? static ?int ? a = 10; /*其實這個語句在函數運行時并沒有執行,它是提供給系統(編譯 ?? ? ? ? ? ? ? ? ? ? ? ?? ? ? 器)一個初值10,然后再程序啟動之前,把此變量初始化。*/
}
動態存儲區: 程序在運行時,根據需要才分配的,比如,用戶用malloc等動態申請的內存(系統堆,但用戶控制),系統控制的函數調用棧。
所以,
全局變量: ?就是定義在函數外的,且內存在靜態存儲區,作用范圍:定義他的位置到文件結束。
局部變量:定義在塊內(函數是塊的最高境界),分為普通的局部變量(就是在棧中),和靜態局部變量(在靜態存儲區),作用范圍:定義它的位置到塊結束。
?? 注意: 很多人會不知道,什么時候用局部變量,什么時候用全局變量,少用全局變量,多用局部變量。全局變量是放在靜態存儲區中,定義了全局變量,系統就少了一個可以利用數據存儲空間,太多全局變量,會導致編譯器無足夠內存分配;而局部變量,可以在不同的模塊中重用(合理分配),其次從模塊的耦合性來說,全局變量使模塊的耦合性增加,模塊獨立性不高,還有在多線程程序里面,一般都建議不要用全局變量,這樣可能造成線程訪問沖突,加鎖等很多問題,但這個東西是相對的,有時用全局變量已有好處,比如全局變量的空間范圍一般比函數棧空間要大(和編譯器有關),有時可能棧要溢出。如果函數要反復經常調用,函數里面有很多的局部變量,這些變量就可能要求棧反復地申請和釋放,這樣也耗時,所以這個問題要自己去體會,根據實際情況分析。
生命期: ?就是一個變量內存分配到內存釋放(我們稱為死亡)的時間段。全局變量和局部靜態變量一般是和程序共存亡,普通的局部變量一般是和塊共存亡。
?注意:雖然局部靜態變量離開他的塊時是不能訪問的,但他并沒有死亡,我們可以通過其他手段來訪問它(嘿嘿)。
存儲類型: 這個后面會有詳細的講解,現在只是說一下,其實在32系統中(其他可以類推),系統內存就是一個數組,每個地址對應一個字節。然后,為了讓變量掌握的字節個數不同(提供更多給接口方便我們使用),系統為了區分它們,就給他們取了不同的名字(char,short,int,long等),然后在用的時候,就從它們的起始地址取出所占的字節數,然后再按一定語意來解析它們(之前協商好的協議(自己的理解))。
?
?
?好,現在來具體講哈
auto , ?register, ?static, ? extern
4個關鍵字
auto: 從名字上,我們稱為自動變量,一般都叫局部變量,變量的默認屬性就是auto,所以一般都沒有用。
register: 就是定義寄存器變量,此變量有個例外,就是如果他真的稱為寄存器變量,那么他存在于寄存器中,就不是在內存中,所以沒有地址。此關鍵字會讓系統盡量合適地把此變量作為寄存器變量(為什么不是一定、而是盡量,原因有很多,比如寄存器有限等),目的是提高訪問速度。當一個變量被頻繁讀/寫時,需要反復訪問內存,花費大量存取時間。為了提高訪問效率,可以使用CPU寄存器變量,不需要訪問內存,直接進行讀/寫,這個關鍵字在嵌入式程序設計中經常會用到。
??注意:
1) 只有局部自動變量和形參才可以定義為寄存器變量。因為寄存器變量屬于動態存儲方式,因此凡需要采用靜態存儲方式變量都不能定義為寄存器變量。
但是使用register修飾符有幾點限制
2) register變量必須是能被CPU所接受的類型。
這通常意味著register變量必須是一個單個的值,并且長度應該小于或者等于整型的長度。不過,有些機器的寄存器也能存放浮點數。
3)因為register變量可能不存放在內存中,所以不能用“&”來獲取register變量的地址。
在調用一個函數時占用一些寄存器以存放寄存器變量的值,函數調用結束后釋放寄存器。此后,在調用另外一個函數時又可以利用這些寄存器來存放該函數的寄存器變量。
4)由于寄存器的數量有限(不同的類型cpu(Intel系列,ARM系列,PowerPC系列等)寄存器數目不一),不能定義任意多個寄存器變量,而且某些寄存器只能接受特定類型的數據(如指針和浮點數),因此真正起作用的register修飾符的數目和類型都依賴于運行程序的機器,而任何多余的register修飾符都將被編譯程序所忽略。
5) 早期的C編譯程序不會把變量保存在寄存器中,除非你命令它這樣做,這時register修飾符是C語言的一種很有價值的補充(由于歷史原因)。然而,隨著編譯程序設計技術的進步,在決定哪些變量應該被存到寄存器中時,現在的C編譯環境能比程序員做出更好的決定。實際上,許多編譯程序都會忽略register修飾符,因為盡管它完全合法,但它僅僅是暗示而不是命令。
?
static: ?此關鍵字很重要
對于變量:
1 static的變量存儲在靜態區,所以變量的值,有持久性,不會隨著塊的離開而消失;?
2 變量被隱藏,如果是全局變量(此針對于多個文件)可見性就是定義他那個文件,其他文件不可訪問,不可以用extern來引用,反過來就是說,如果沒有static關鍵字,其他文件就可以通過extern來引用;
3 變量沒有定義初值,系統(編譯器)會默認設置初值為0;
?
對于函數: 沒有static修飾的函數,在多個文件編譯時,具有全局可見性(這個讀者自己可以試試)而有static修飾的函數,只是在本文件可見,對其他文件隱藏。
?
extern : 上面很多地方提到, 對于全局變量如果不在作用域內,extern ?可以擴大他的作用域。例如,在單個文件中,在全局變量之上地方可以用extern來引用下面定義的全局變量,而在多個文件中,extern可以引用其他文件的全局變量。
??注意: ?extern能否引用其他文件的全局變量,要看其他文件的全局變量是否隱藏,很顯然局部變量是不能引用的。
?
聲明:
?? ?本文完全是為了學習而誕生的, 我們只有不斷理解,不斷地從錯誤的認識中清醒過來,才可能更好使用它,希望對你們有用,要成為頂級的c程序員這些只是,記住只是基礎中的基礎,還有很多的東西有待我們去研究實踐,這路還很長。
? 思考小問題:
?? ? ? c語言里的一個遞歸問題,怎么從第20層直接返回到第17層,保證程序正常運行?
?
?
總結
以上是生活随笔為你收集整理的顶级c程序员之路 基础篇 - 第一章 关键字的深度理解 number-1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 顶级c程序员之路 选学篇-1 深入理解
- 下一篇: 深入理解C++内存管理