關於經典面試一年多少秒的思考!啟發#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 有何區別?看到下面的解釋還是有區別的
C語言整形常量定義
2014年09月01日 19:16:41 atleks 閱讀數:3308
在書中看到這樣一道題目:用預處理指令#define宣告一個常數,用以表明一年中有多少秒。書中給的答案是:
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
寫一個小程式測試此巨集定義,如下:
-
#include <stdio.h>
-
#define SECONDS_PER_YEAR (60*60*24*365)UL
-
int main()
-
{
-
printf("%lu\r\n", SECONDS_PER_YEAR);
-
return 0;
-
}
使用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編譯器,下面給個師哥的關於巨集定義資料溢位的測試結果筆記連結:
通過筆記也可以加深對資料溢位的理解!