預先處理–》編譯–》啟動並執行區別

來源:互聯網
上載者:User

相信很多人懂這個問題,也很多人沒想過,包括我,今天看書想到了就寫下來。先看程式(抱歉在linux下沒有找到舒服的可以複製terminal的工具,只好了,將就著看看)


注釋的就先不看了,看那幾行沒有注釋的enum coordinate_type 表示一個枚舉(Enumeration)類型。枚舉類型的成員是常量,它們的值由編譯器自動分配,例如定義了上面的枚舉類型之後,RECTANGULAR就表示常量0,POLAR 表示常量1。如果不希望從0開始分配,可以這樣定義:enum coordinate_type { RECTANGULAR = 1,
POLAR };

這樣,RECTANGULAR就表示常量1,而POLAR 表示常量2。枚舉常量也是一種整型,其值在編譯時間確定,因此也可以出現在常量運算式中,可以用於初始化全域變數或者作為case 分支的判斷條件。


有一點需要注意,雖然結構體的成員名和變數名不在同一命名空間中,但枚舉的成員名卻和變數名在同一命名空間中,所以會出現命名衝突。那什麼是命名空間呢?我的理解是在運行程式時會為每一個函數開闢一個函數幀棧,局部變數之類的可以在這裡賦值運算等,如果在這個函數幀棧裡同個等級裡(指的是不再加{}構成語句塊)同樣的命名會造成衝突的那就屬於同個命名空間,如上所述,結構體的成員名跟某個變數名命令重複是不會衝突的,而枚舉類型成員名跟某個變數名重複是會造成衝突的,如編譯時間會提示錯誤如下:


那如果加了{}呢,如:


再次編譯,提示就不一樣了:


這時就不會提示發生衝突,大家都知道如果函數內的局部變數跟全域變數重名,則在函數內全域變數被屏蔽了,這裡也是同樣的道理,就是在函數內{}語句塊也屏蔽了外圍的,裡所應當的是函數的局部變數等函數調用完後儲存空間就會釋放,而{}裡面更快釋放,可以看到列印完之后里面的rectanger變數就會被釋放,但polar變數得等整個函數調用完畢才會釋放,因為這裡使用的是枚舉類型中的成員。


那這裡提示警告,是否能運行呢?當然了,因為只要不出現錯誤只出現警告是可以產生可執行檔的,只是有警告就意味著程式有bug,是很危險的。這裡的意思是因為局部變數rectanger沒有初始化,所以運行列印時會是不確定的值,即每次運行都可能是不一樣的結果,要記住:局部變數是函數調用時才賦值的!局部變數儲存空間地址也許會隨著每次函數調用時而不同,如果你設定了初值,那空間怎麼變裡面的值都是你賦予的那個,但如果沒有初始化,那每次運行都是不確定的值。如:


下面看把枚舉類型寫在函數外面的情況:



編譯一下,看看出現什麼提示:


可以看到沒有發生命名衝突,只是還是提示沒有初始化的問題,因為在這裡的枚舉類型是全域變數,不會跟局部變數命名衝突,但是會被覆蓋掉。
這裡需要注意的是:全域變數都是靜態分配的,就是說編譯的時候已經確定了初值,全域變數定義在所有的函數體之外,它們在程式開始運行時分配儲存空間,在程式結束時釋放儲存空間,在任何函數中都可以訪問全域變數,如果全域變數前面加個首碼static則表示此變數是local的而不是global的,意思是不能被其他檔案所調用;   


如果前面加首碼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段。


下面看預先處理:


看看編譯會提示什麼:


很明顯就是因為宏定義了rectanger,如果有重名的話,宏定義覆蓋所有其它標識符,因為它在預先處理階段而不是
編譯階段處理,所以在函數裡面重新定義rectanger會提示表明這個變數名已經是個常量了。

我們可以使用 gcc -E來查看預先處理後而編譯前的東西,一看這麼多頁螢幕都看不完整加個less查看,居然有好幾螢幕,只截取最後面的一部分來看:


是不是發現了啊,預先處理的時候已經把rectanger 都替換成宏定義中的 1了,所以接下去進行編譯時間當然會報錯了,因為你在 int 1啊,能不錯嗎?那前面輸出的是什麼東西呢,其實就是將一些頭函數進行展開。

反正處理的步驟就是 預先處理 --》 編譯 --》 運行,但步驟的不同是涉及到很多東西的,比如全域變數和局部變數的賦值,為什麼全域變數只能用常量來初始化而局部變數可以用帶數學函數的運算式來初始化呢?如double pi = acos(-1.0); 因為程式開始運行時要用適當的值來初始化全域變數,所以初始值必須儲存在編譯產生的可執行檔中,因此初始值在編譯時間就要計算出來,然而上面那種Initializer的值必須在程式運行時調用
acos函數才能得到,所以不能用來初始化全域變數。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.