C語言中TMin的寫法


  在看《深入理解計算機系統》第二版中文版時(Computer Systems A Programmer's Perspective Second Edititon),看到48頁第二章網絡旁注中提到:
C語言中,將TMin32(32位有符號整數的最小值)寫成 -2147483647-1。為什么不簡單地寫成 -2147483648 或者 0x80000000 ?
  書中提到是由於補碼表示的不對稱性和C語言轉換規則之間奇怪的交互。補碼表示不對稱性CSAPP講解的通俗易懂,但這里面涉及到什么樣的C語言轉換規則,書中卻沒有說明。

  在這篇博文的寫作過程中,搜到了一些很有用的資料,請見本文末尾的參考資料一節。本文中很大部分內容都是參照 CS:APP Web Aside DATA:TMIN:
Writing TMin in C 一文來寫的,然后根據自己的理解添加了一些細節上的東西,力求能夠更加通俗易懂一些,這就是本文存在的意義。

  C語言中整型常量的實際類型

  首先來看一下C語言中整型常量的定義。

  C99標准中 6.4.4.1 Integer constants 中提到:
    An integer constant begins with a digit, but has no period or exponent part. It may have a
prefix that specifies its base and a suffix that specifies its type.

  可見如果不發生溢出,整型常量的值總是非負數。如果前面出現符號,則是對整型常量使用的一元運算符,而不是整型常量的一部分。

  整型常量的實際類型取決於長度、基數、后綴字母和C語言實現確定的類型表示精度。確定整數常量類型的規則比較復雜,並且在非標准C、C89和C99中是不相同的。具體規則可見<<C語言參考手冊》第五版 第二章 2.7.1 整型常量一節。對於本文中提到的場景,下面的這個整數常量的類型表就夠用了。

        表一:整數常量的類型

 ISO C90 ISO C99
十進制(Decimal) 十六進制(Hexadecimal) 十進制(Decimal) 十六進制(Hexadecimal)
int
long
unsigned
unsigned long
int
unsigned
long
unsigned long
int
long
long long
 
int
unsigned
long
unsigned long
long long
unsigned long long

  根據C語言版本和常量的格式(十進制和十六進制),常量的數據類型是從上面表格里選擇第一個最合適(能表示常量而不溢出的)的類型。

  對於ISO C90,編譯器依次嘗試int 、long、 unsigned(32位機器上long跟int一樣,是32位), 最終選擇unsigned來表示。對於 2147483648 和 -2147483648,如果表示為32位的二進制數字,它們的位表示是一樣的,都是0x80000000。所以這個常量表達式(-2147483648)的數據類型為unsigned且值為 2147483648。

  對於ISO C99,編譯器依次選擇 int、long、long long,最終選擇long long類型才能容納 2147483648 。用64位,可以唯一表示 2147483648 和 -2147483648,所以這個常量表達式的數據類型為long long,值為 -2147483648。

  對於16進制常數 0x80000000(注意,按照C語言中整型常量的定義,這個整數常量是正數,值為2417483648),在32位機器上,編譯器也是利用同樣的規則,依照表一中的16進制的列表來處理。兩個語言標准中,都是首先跟TMax32(0x7FFFFFFF)比較,由於0x80000000更大,所以這個值不能用int來表示。接下來和UMax32(0xFFFFFFFF)比較,由於比它小一些,所以選擇unsigned來表示。所以這個常量表達式的數據類型是unsigned,值為0x80000000(或者說,是等於2147483648)。

  在64位的機器上,事情稍微有些不同。兩個語言標准中,十進制的格式 -2417483648 都是long(64位)類型,值為 -2417483648,然而十六進制格式 0x80000000 都是unsigned類型,值為0x80000000(或者說,是 2147483648)。

  用一句話來解釋C語言中TMin32的古怪寫法的原因:雖然-2147483648 這個數值能夠用int類型來表示,但在C語言中卻沒法寫出對應這個數值的int類型常量。

  C語言中如何正確表示TMin32呢?  

  C語言中limits.h中定義了如下兩個宏: 

#define INT_MAX 2147483647
#define INT_MIN (-INT_MAX - 1)

    ISO C99在stdint.h文件中定義了一些宏 INTN_C、UINTN_C、INTMAX_C與UINTMAX_C,提供對整型常量的長度與類型的可移植性控制。

  知道這個有什么用?:

  1. 考慮如下代碼:

int dcomp = (-2147483648 < 0);
int hcomp = (0x80000000 < 0);

  請大家思考一下dcomp和hcomp的值是0還是1?需要考慮編譯器指定的C語言版本和機器位數(字的大小)。

  gcc 中,C模式默認的C語言標准是gnu89(ISO C90, 包括一些C99特性)。可以通過 -std 選項來指定C語言的版本。

  MSVC支持C90,只支持部分的C99特性。

  2. 考慮如下代碼:

int dtmin = -2147483648;
int dcomp2 = (dtmin < 0);
int htmin = 0x80000000;
int hcomp2 = (htmin < 0);

  如果你親自在32位和64位機器上用ISO-C90和ISO-C99版本來編譯運行一下,會發現,所有情況下,dcomp2和hcomp2的值都是1。你能解釋一下為什么嗎?

 

  廣告一下吧,在搜 TMin 資料的時候,發現了一本書《一站式學習C編程》,網上有第一版的html的版本,我只看了其中介紹整型  的一節,發現作者寫的通俗易懂,而且是國人寫的,必須得支持啊。

  參考資料:      

    C語言參考手冊第五版 2.7.1 整型常量
       深入理解計算機系統 2.2.3 補碼編碼

    在c語言中寫TMin

  補充閱讀:    

  由於補碼的不對稱性,導致在用補碼來表示負數的編譯器中使用abs()函數會有問題
     如果TMin32/-1,也會導致同樣的問題

  整數常量的類型造成的一個隱晦的問題

 


如果您看了本篇博客,覺得對您有所收獲,請點擊右下角的“推薦”,讓更多人看到!

資助Jack47寫作,打賞一個雞蛋灌餅錢吧
pay_weixin
微信打賞
pay_alipay
支付寶打賞

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM