1. 程式人生 > >關於經典面試一年多少秒的思考!啟發#define與UL!整形常量的定義

關於經典面試一年多少秒的思考!啟發#define與UL!整形常量的定義

關於經典面試一年多少秒的思考!啟發#define與UL!

2016年01月11日 13:52:03 Agou_66 閱讀數:1935 標籤: C語言#defineUL巨集定義一年多少秒 更多

個人分類: C語言

版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/Agou_66/article/details/50496550

預處理-巨集定義一講中,一道經典面試題巨集定義一年有多少秒?由此引發知識點的模糊,所以寫部落格作為記錄,也共大家學習!

此題不考慮閏年問題,如下:

 

#define  SEC_PER_YEAR  356*24*60*60

 

 

那麼問題來了,到底這樣定義好不好,我們知道巨集定義是在預處理的時候做的,在預處理時他會將字串SEC_PER_YEAR替換成356*24*60*60。

程式如圖:

經過gcc -E 預編譯後如圖:

結果是:

為了資料的完整加()能讓此巨集定義更加安全,C語言預設巨集定義的整形數字是 int,為了讓該巨集定義完美執行在8位、16位、32位、64位機器中將巨集改進為(這句是很關鍵的,這就是機器不管是多少位,只要將這個數加上UL就會按照無符號數進行輸出,就不會有溢位的的現象了,到底UL和unsigned long int 有何區別?看到下面的解釋還是有區別的

。UL值只能修飾"整形常量"的,不能修飾非整形常量,到底何為整形常量:3*4*6這個式子是不是整形常量呢?,看下面紫色的文字,“”UL值只能修飾"整形常量"“”的說法不正確,好像與整形常量沒有多大關係,只是UL先給誰結合的問題,查百度:

C語言整形常量定義

2014年09月01日 19:16:41 atleks 閱讀數:3308

在書中看到這樣一道題目:用預處理指令#define宣告一個常數,用以表明一年中有多少秒。書中給的答案是:

 

#define  SECONDS_PER_YEAR  (60 * 60 * 24 * 365)UL

 

 

寫一個小程式測試此巨集定義,如下:

 
  1. #include <stdio.h>

  2.  
  3. #define SECONDS_PER_YEAR (60*60*24*365)UL

  4.  
  5. int main()

  6. {

  7. printf("%lu\r\n", SECONDS_PER_YEAR);

  8. return 0;

  9. }


使用GCC編譯時報如下錯誤: error: ‘SECONDS_PER_YEAR’ undeclared (first use in this function)

查閱一下C99標準(當然,也可以查閱最新的C標準C11,不過我使用的編譯器大都是隻支援到C99的),在6.4.4節講到了關於常量的定義,檢視其

關於整形常量的定義,如下:

對於十進位制整數常量,其定義的形式為:decimal-constant  integer-suffixopt, 在decimal-constant和integer-suffix之間並沒有括號,這裡的integer-suffix是整形常量的字尾名,譬如:45UL,45*34*23UL這兩種情況都可以,但是如果寫成(45*34*23)UL就錯了,因為在整形常量和字尾名之間加上括號,這樣就執行括號內的計算,然後再與UL結合,前面的計算就溢位(前提是int 的位數是16位,不能表示這個數了,如果寫成45UL,45*34*23UL,系統首先是23和UL結合,這就認為這個數是unsigned long int 型別,進行了型別提升,符合了C語言算術計算中的型別提升原則,也就是說這種型別的表示的範圍被擴大了,前面的數字再乘進來,就會按照unsigned long int型別的範圍來表了,)了,再加上UL還有什麼意義呢?。所以,文章

開頭提到的書中給出的例題的答案是錯誤的。下面詳細講解一下有關C語言中整形常量的定義。

在C語言中的整形常量,關於其預設型別,參考C99標準中6.4.4.1節:

The type of an integer constant is the first of the corresponding list in which its value can be represented.

即,C語言中一個十進位制整數會被預設為int型別,如果長度超出int表示範圍,而又在long int的範圍之內,則會被認為是long int型別,如果long int

也表示不了,則會被認為是long long int型別。對於很多在常見的32位系統上程式設計的人來說可能結尾處加UL的作用並不明顯,因為int和long都是32位,

但是對於嵌入式環境程式設計的人來說,這個就很有必要了,因為程式跑的環境有可能是16位的,且int是16位而long是32位的,這個時候加UL字尾就很有必要了

對於巨集定義:

 

#define SECONDS_PER_YEAR  60 * 60 * 24 * 365

則60*60*24*365的結果會被認為是一個int,如果是16位的環境則會溢位。如果在結尾加上UL,如下:

 

 

#define SECONDS_PER_YEAR  60 * 60 * 24 * 365UL

根據上述C99標準中有關整形常量定義可以知道其實UL首先是與365結合,即365會被認為是一個unsigned long型別的數,而根據C語言算術計算中的型別提升

 

原則可以知道整個算式的計算結果也會被認為是UL型別的。

對於巨集定義:

 

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

之所以會報錯是因為,UL找不到結合的數字,在16位的環境中,先計算60×60×24×365,則會溢位,然後再將這個溢位的值與UL結合,已經沒有意義,故編譯

 

會報錯。

 

參考資料:

[1]   C99標準

[2]   http://bbs.21ic.com/icview-196689-1-1.html

[3]   http://blog.csdn.net/ropenyuan/article/details/6157589

):

 

#define  SEC_PER_YEAR  (356*24*60*60)UL

 

在去編譯,你會發現出錯了!錯誤是  error: expected ‘,’ or ‘;’ before ‘UL’明明對著呢,為啥會錯呢!就因為UL,UL告訴編譯器(是編譯器非預編譯器)此時是unsigned long int型別,U和L是 “整數常量” 的字尾修飾,因此UL只能修飾整數常量本身;所以將巨集寫為:

 

#define  SEC_PER_YEAR  (356*24*60*60UL)

 

 

此時在編譯發現完美執行,這回美了,定義出個完美的巨集,錯!在C51編譯器中int是16

位的,int最大值是32767,然而356*24*60 是int型別已經溢位,所以依然得不得完美的巨集。所以次巨集改為:

 

#define  SEC_PER_YEAR  (1UL*356*24*60*60)

 

 

這次終於完美了;1UL保證以後的資料不會在任何情況下溢位!(為什麼要加1UL呢?,我認為上面文字中紫色的文字內容講的有道理。

 

由於我沒有51編譯器,下面給個師哥的關於巨集定義資料溢位的測試結果筆記連結:

                                 阿樹師兄的筆記

通過筆記也可以加深對資料溢位的理解!