相信很多人懂這個問題,也很多人沒想過,包括我,今天看書想到了就寫下來。先看程式(抱歉在linux下沒有找到舒服的可以複製terminal的工具,只好截圖了,將就著看看)
注釋的就先不看了,看那幾行沒有注釋的enum coordinate_type 表示乙個列舉(enumeration)型別。列舉型別的成員是常量,它們的值由編譯器自動分配,例如定義了上面的列舉型別之後,rectangular就表示常量0,polar 表示常量1。如果不希望從0開始分配,可以這樣定義:enum coordinate_type ;
這樣,rectangular就表示常量1,而polar 表示常量2。列舉常量也是一種整型,其值在編譯時確定,因此也可以出現在常量表示式中,可以用於初始化全域性變數或者作為case 分支的判斷條件。注意:列舉常量是不占用記憶體的,它們在編譯時被全部求值,只有定義了enum 變數才會占用記憶體。
有一點需要注意,雖然結構體的成員名和變數名不在同一命名空間中,但列舉的成員名卻和變數名在同一命名空間中,所以會出現命名衝突。那什麼是命名空間呢?我的理解是在執行程式時會為每乙個函式開闢乙個函式幀棧,區域性變數之類的可以在這裡賦值運算等,如果在這個函式幀棧裡同個等級裡(指的是不再加{}構成語句塊)同樣的命名會造成衝突的那就屬於同個命名空間,如上所述,結構體的成員名跟某個變數名命令重複是不會衝突的,而列舉型別成員名跟某個變數名重複是會造成衝突的,如編譯時會提示錯誤如下:
那如果加了{}呢,如:
再次編譯,提示就不一樣了:
這時就不會提示發生衝突,大家都知道如果函式內的區域性變數跟全域性變數重名,則在函式內全域性變數被遮蔽了,這裡也是同樣的道理,就是在函式內{}語句塊也遮蔽了外圍的,裡所應當的是函式的區域性變數等函式呼叫完後儲存空間就會釋放,而{}裡面更快釋放,可以看到列印完之後裡面的rectanger變數就會被釋放,但polar變數得等整個函式呼叫完畢才會釋放,因為這裡使用的是列舉型別中的成員。
那這裡提示警告,是否能執行呢?當然了,因為只要不出現錯誤只出現警告是可以生成可執行檔案的,只是有警告就意味著程式有bug,是很危險的。這裡的意思是因為區域性變數rectanger沒有初始化,所以執行列印時會是不確定的值,即每次執行都可能是不一樣的結果,要記住:區域性變數是函式呼叫時才賦值的!區域性變數儲存空間位址也許會隨著每次函式呼叫時而不同,如果你設定了初值,那空間怎麼變裡面的值都是你賦予的那個,但如果沒有初始化,那每次執行都是不確定的值。如下圖:
下面看把列舉型別寫在函式外面的情況:
可以看到沒有發生命名衝突,只是還是提示沒有初始化的問題,因為在這裡的列舉常量是全域性的,不會跟區域性變數命名衝突,但是會被覆蓋掉。
如果前面加字首const如 const int a; 表明是唯讀的,注意,像a這種const 變數在定義時必須初始化如const int a = 100;。因為只有初始化時才有機會給它乙個值,對於全域性來說一旦定義之後就不能再改寫了,也就是不能再賦值了,編譯通過但執行時會出現段錯誤。而如果a 是區域性變數則可以通過 int *p = &a; *p = 200;來改寫,編譯通過且可以執行。其實加了關鍵字const只是提示編譯器這個變數是常量,如果我們在接下來的操作中試圖更改它,編譯器會報錯,而並不是真正的常量,上面的例子也說明通過指標也是可以更改的,什麼情況下完全不能修改呢,當a是加const限定且初始化的全域性變數,此時a位於.rodata段
還有個特例就是:函式中的static變數不同於以前我們講的區域性變數,它並不是在呼叫函式時分配,在函式返回時釋放,而是像全域性變數一樣靜態分配,所以用「static」(靜態)這個詞。另一方面,函式中的static變數的作用域和以前講的區域性變數一樣,只在函式中起作用。
全域性變數的作用域從開始定義的地方到檔案的末尾,在任何函式中都可以訪問全域性變數,整個程式執行完畢會釋放全域性變數的儲存空間,當然同時還有**的儲存空間也會被釋放,而區域性變數的儲存空間早在他們之前釋放; 如果全域性變數沒有賦予初值,則初始化為0,位於.bss段。
如果全域性變數前面加個字首static則表示此變數是local的而不是global的,意思是不能被其他檔案所呼叫。
下面看預處理:
看看編譯會提示什麼:
很明顯就是因為巨集定義了rectanger,如果有重名的話,巨集定義覆蓋所有其它識別符號,因為它在預處理階段而不是
編譯階段處理,所以在函式裡面重新定義rectanger會提示表明這個變數名已經是個常量了。
我們可以使用 gcc -e來檢視預處理後而編譯前的東西,一看這麼多頁螢幕都看不完整加個less檢視,居然有好幾螢幕,只擷取最後面的一部分來看:
是不是發現了啊,預處理的時候已經把rectanger 都替換成巨集定義中的 1了,所以接下去進行編譯時當然會報錯了,因為你在 int 1啊,能不錯嗎?那前面輸出的是什麼東西呢,其實就是將一些頭函式進行展開。
反正處理的步驟就是 預處理 --》 編譯 --》 執行,但步驟的不同是涉及到很多東西的,比如全域性變數和區域性變數的賦值,為什麼全域性變數只能用常量來初始化而區域性變數可以用帶數學函式的表示式來初始化呢?如double pi = acos(-1.0); 因為程式開始執行時要用適當的值來初始化全域性變數,所以初始值必須儲存在編譯生成的可執行檔案中,因此初始值在編譯時就要計算出來,然而上面那種initializer的值必須在程式執行時呼叫 acos函式才能得到,所以不能用來初始化全域性變數。
預處理 編譯 連線 執行
1.預處理階段 編譯器以c檔案作為乙個單元,首先讀這個c檔案,發現第一句與第二句是包含乙個標頭檔案,就會在所有搜尋路徑中尋找這兩個檔案,找到之後,就會將相應標頭檔案中再去處理巨集,變數,函式宣告,巢狀的標頭檔案包含等,檢測依賴關係,進行巨集替換,看是否有重複定義與宣告的情況發生,最後將那些檔案中所有...
編譯預處理
所謂編譯預處理,就是在c源程式的編譯之前,由編譯預處理程式對這些編譯預處理命令進行處理的過程。最常見的就是常量的替換。編譯預處理按功能可以分為巨集定義,檔案包含和條件編譯三類。編譯預處理命令以 開頭,下面進行詳細說明 一.巨集定義與符號常量 1.無參巨集定義 define 識別符號 字串 1 巨集名...
編譯預處理
1.巨集定義指令 1 定義變數與命令 避免幻數 在巨集定義命名時,盡量能清楚的表明功能,大寫 不能以 開頭易與內建巨集衝突 2 定義巨集函式 define max a,b a b a b int num max 6,5 6 5 6 5 用編譯時間換記憶體空間的是巨集函式 用記憶體空間換執行空間的是內...