<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 第?15?章?數據類型詳解 **目錄** + [1\. 整型](ch15s01.html) + [2\. 浮點型](ch15s02.html) + [3\. 類型轉換](ch15s03.html) + [3.1\. Integer Promotion](ch15s03.html#id2757955) + [3.2\. Usual Arithmetic Conversion](ch15s03.html#id2758200) + [3.3\. 由賦值產生的類型轉換](ch15s03.html#id2758516) + [3.4\. 強制類型轉換](ch15s03.html#id2758655) + [3.5\. 編譯器如何處理類型轉換](ch15s03.html#id2758764) ## 1.?整型 我們知道,在C語言中`char`型占一個字節的存儲空間,一個字節通常是8個bit。如果這8個bit按無符號整數來解釋,取值范圍是0~255,如果按有符號整數來解釋,采用2's Complement表示法,取值范圍是-128~127。C語言規定了`signed`和`unsigned`兩個關鍵字,`unsigned char`型表示無符號數,`signed char`型表示有符號數。 那么以前我們常用的不帶`signed`或`unsigned`關鍵字的`char`型是無符號數還是有符號數呢?C標準規定這是Implementation Defined,編譯器可以定義`char`型是無符號的,也可以定義`char`型是有符號的,在該編譯器所對應的體系結構上哪種實現效率高就可以采用哪種實現,x86平臺的`gcc`定義`char`型是有符號的。這也是C標準的Rationale之一:_優先考慮效率,而可移植性尚在其次_。這就要求程序員非常清楚這些規則,如果你要寫可移植的代碼,就必須清楚哪些寫法是不可移植的,應該避免使用。另一方面,寫不可移植的代碼有時候也是必要的,比如Linux內核代碼使用了很多只有`gcc`支持的語法特性以得到最佳的執行效率,在寫這些代碼的時候就沒打算用別的編譯器編譯,也就沒考慮可移植性的問題。如果要寫不可移植的代碼,你也必須清楚代碼中的哪些部分是不可移植的,以及為什么要這樣寫,如果不是為了效率,一般來說就沒有理由故意寫不可移植的代碼。從現在開始,我們會接觸到很多Implementation Defined的特性,C語言與平臺和編譯器是密不可分的,離開了具體的平臺和編譯器討論C語言,就只能討論到本書第一部分的程度了。注意,ASCII碼的取值范圍是0~127,所以不管`char`型是有符號的還是無符號的,存一個ASCII碼都沒有問題,一般來說,如果用`char`型存ASCII碼字符,就不必明確寫是`signed`還是`unsigned`,如果用`char`型表示8位的整數,為了可移植性就必須寫明是`signed`還是`unsigned`。 ### Implementation-defined、Unspecified和Undefined 在C標準中沒有做明確規定的地方會用Implementation-defined、Unspecified或Undefined來表述,在本書中有時把這三種情況統稱為“未明確定義”的。這三種情況到底有什么不同呢? 我們剛才看到一種Implementation-defined的情況,C標準沒有明確規定`char`是有符號的還是無符號的,但是要求編譯器必須對此做出明確規定,并寫在編譯器的文檔中。 而對于Unspecified的情況,往往有幾種可選的處理方式,C標準沒有明確規定按哪種方式處理,編譯器可以自己決定,并且也不必寫在編譯器的文檔中,這樣即便用同一個編譯器的不同版本來編譯也可能得到不同的結果,因為編譯器沒有在文檔中明確寫它會怎么處理,那么不同版本的編譯器就可以選擇不同的處理方式,比如下一章我們會講到一個函數調用的各個實參表達式按什么順序求值是Unspecified的。 Undefined的情況則是完全不確定的,C標準沒規定怎么處理,編譯器很可能也沒規定,甚至也沒做出錯處理,有很多Undefined的情況編譯器是檢查不出來的,最終會導致運行時錯誤,比如數組訪問越界就是Undefined的。 初學者看到這些規則通常會很不舒服,覺得這不是在學編程而是在啃法律條文,結果越學越泄氣。是的,C語言并不像一個數學定理那么完美,現實世界里的東西總是不夠完美的。但還好啦,C程序員已經很幸福了,只要嚴格遵照C標準來寫代碼,不要去觸碰那些陰暗角落,寫出來的代碼就有很好的可移植性。想想那些可憐的JavaScript程序員吧,他們甚至連一個可以遵照的標準都沒有,一個瀏覽器一個樣,甚至同一個瀏覽器的不同版本也差別很大,程序員不得不為每一種瀏覽器的每一個版本分別寫不同的代碼。 除了`char`型之外,整型還包括`short int`(或者簡寫為`short`)、`int`、`long int`(或者簡寫為`long`)、`long long int`(或者簡寫為`long long`)等幾種<sup>[[25](#ftn.id2756873)]</sup>,這些類型都可以加上`signed`或`unsigned`關鍵字表示有符號或無符號數。其實,對于有符號數在計算機中的表示是Sign and Magnitude、1's Complement還是2's Complement,C標準也沒有明確規定,也是Implementation Defined。大多數體系結構都采用2's Complement表示法,x86平臺也是如此,從現在開始我們只討論2's Complement表示法的情況。還有一點要注意,除了`char`型以外的這些類型如果不明確寫`signed`或`unsigned`關鍵字都表示`signed`,這一點是C標準明確規定的,不是Implementation Defined。 除了`char`型在C標準中明確規定占一個字節之外,其它整型占幾個字節都是Implementation Defined。通常的編譯器實現遵守ILP32或LP64規范,如下表所示。 **表?15.1.?ILP32和LP64** | 類型 | ILP32(位數) | LP64(位數) | | --- | --- | --- | | char | 8 | 8 | | short | 16 | 16 | | int | 32 | 32 | | long | 32 | 64 | | long long | 64 | 64 | | 指針 | 32 | 64 | ILP32這個縮寫的意思是`int`(I)、`long`(L)和指針(P)類型都占32位,通常32位計算機的C編譯器采用這種規范,x86平臺的`gcc`也是如此。LP64是指`long`(L)和指針占64位,通常64位計算機的C編譯器采用這種規范。指針類型的長度總是和計算機的位數一致,至于什么是計算機的位數,指針又是一種什么樣的類型,我們到[第?17?章 _計算機體系結構基礎_](ch17.html#arch)和[第?23?章 _指針_](ch23.html#pointer)再分別詳細解釋。從現在開始本書做以下約定:_在以后的陳述中,缺省平臺是x86/Linux/gcc,遵循ILP32,并且`char`是有符號的,我不會每次都加以說明,但說到其它平臺時我會明確指出是什么平臺_。 在[第?2?節 “常量”](ch02s02.html#expr.constant)講過C語言的常量有整數常量、字符常量、枚舉常量和浮點數常量四種,其實字符常量和枚舉常量的類型都是`int`型,因此前三種常量的類型都屬于整型。整數常量有很多種,不全是`int`型的,下面我們詳細討論整數常量。 以前我們只用到十進制的整數常量,其實在C語言中也可以用八進制和十六進制的整數常量<sup>[[26](#ftn.id2757137)]</sup>。八進制整數常量以0開頭,后面的數字只能是0~7,例如022,因此十進制的整數常量就不能以0開頭了,否則無法和八進制區分。十六進制整數常量以0x或0X開頭,后面的數字可以是0~9、a~f和A~F。在[第?6?節 “字符類型與字符編碼”](ch02s06.html#expr.charencoding)講過一種轉義序列,以\或\x加八進制或十六進制數字表示,這種表示方式相當于把八進制和十六進制整數常量開頭的0替換成\了。 整數常量還可以在末尾加u或U表示“unsigned”,加l或L表示“long”,加ll或LL表示“long long”,例如0x1234U,98765ULL等。但事實上u、l、ll這幾種后綴和上面講的`unsigned`、`long`、`long long`關鍵字并不是一一對應的。這個對應關系比較復雜,準確的描述如下表所示(出自[[C99]](bi01.html#bibli.c99 "ISO/IEC 9899: Programming Languages - C")條款6.4.4.1)。 **表?15.2.?整數常量的類型** | 后綴 | 十進制常量 | 八進制或十六進制常量 | | --- | --- | --- | | 無 | `int` `long?int` `long?long?int` | `int` `unsigned?int` `long?int` `unsigned?long?int` `long?long?int` `unsigned?long?long?int` | | u或U | `unsigned?int` `unsigned?long?int` `unsigned?long?long?int` | `unsigned?int` unsigned?long?int` `unsigned?long?long?int` | | l或L | `long?int` `long?long?int` | `long?int` `unsigned?long?int` `long?long?int` `unsigned?long?long?int` | | 既有u或U,又有l或L | `unsigned?long?int` `unsigned?long?long?int` | `unsigned?long?int` `unsigned?long?long?int` | | ll或LL | `long?long?int` | `long?long?int` `unsigned?long?long?int` | | 既有u或U,又有ll或LL | `unsigned?long?long?int` | `unsigned?long?long?int` | 給定一個整數常量,比如1234U,那么它應該屬于“u或U”這一行的“十進制常量”這一列,這個表格單元中列了三種類型`unsigned int`、`unsigned long int`、`unsigned long long int`,從上到下找出第一個足夠長的類型可以表示1234這個數,那么它就是這個整數常量的類型,如果`int`是32位的那么`unsigned int`就可以表示。 再比如0xffff0000,應該屬于第一行“無”的第二列“八進制或十六進制常量”,這一列有六種類型`int`、`unsigned int`、`long int`、`unsigned long int`、`long long int`、`unsigned long long int`,第一個類型`int`表示不了0xffff0000這么大的數,我們寫這個十六進制常量是要表示一個正數,而它的MSB(第31位)是1,如果按有符號`int`類型來解釋就成了負數了,第二個類型`unsigned int`可以表示這個數,所以這個十六進制常量的類型應該算`unsigned int`。所以請注意,0x7fffffff和0xffff0000這兩個常量雖然看起來差不多,但前者是`int`型,而后者是`unsigned int`型。 講一個有意思的問題。我們知道x86平臺上`int`的取值范圍是-2147483648~2147483647,那么用`printf("%d\n", -2147483648);`打印`int`類型的下界有沒有問題呢?如果用`gcc main.c -std=c99`編譯會有警告信息:`warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘long long int’`。這是因為,雖然-2147483648這個數值能夠用`int`型表示,但在C語言中卻沒法寫出對應這個數值的`int`型常量,C編譯器會把它當成一個整數常量2147483648和一個負號運算符組成的表達式,而整數常量2147483648已經超過了`int`型的取值范圍,在x86平臺上`int`和`long`的取值范圍相同,所以這個常量也超過了`long`型的取值范圍,根據上表第一行“無”的第一列`十進制常量`,這個整數常量應該算`long long`型的,前面再加個負號組成的表達式仍然是`long long`型,而`printf`的`%d`轉換說明要求后面的參數是`int`型,所以編譯器報警告。之所以編譯命令要加`-std=c99`選項是因為C99以前對于整數常量的類型規定和上表有一些出入,即使不加這個選項也會報警告,但警告信息不準確,讀者可以試試。如果改成`printf("%d\n", -2147483647-1);`編譯器就不會報警告了,-號運算符的兩個操作數-2147483647和1都是`int`型,計算結果也應該是`int`型,并且它的值也沒有超出`int`型的取值范圍;或者改成`printf("%lld\n", -2147483648);`也可以,轉換說明`%lld`告訴`printf`后面的參數是`long long`型,有些轉換說明格式目前還沒講到,詳見[第?2.9?節 “格式化I/O函數”](ch25s02.html#stdlib.formatio)。 怎么樣,整數常量沒有你原來想的那么簡單吧。再看一個不簡單的問題。`long long i = 1234567890 * 1234567890;`編譯時會有警告信息:`warning: integer overflow in expression`。1234567890是`int`型,兩個`int`型相乘的表達式仍然是`int`型,而乘積已經超過`int`型的取值范圍了,因此提示計算結果溢出。如果改成`long long i = 1234567890LL * 1234567890;`,其中一個常量是`long long`型,另一個常量也會先轉換成`long long`型再做乘法運算,兩數相乘的表達式也是`long long`型,編譯器就不會報警告了。有關類型轉換的規則將在[第?3?節 “類型轉換”](ch15s03.html#type.conversion)詳細介紹。 * * * <sup>[[25](#id2756873)]</sup> 我們在[第?4?節 “結構體和聯合體”](ch19s04.html#asmc.structunion)還要介紹一種特殊的整型--Bit-field。 <sup>[[26](#id2757137)]</sup> 有些編譯器(比如`gcc`)也支持二進制的整數常量,以0b或0B開頭,比如0b0001111,但二進制的整數常量從未進入C標準,只是某些編譯器的擴展,所以不建議使用,由于二進制和八進制、十六進制的對應關系非常明顯,用八進制或十六進制常量完全可以代替使用二進制常量。 ## 2.?浮點型 C標準規定的浮點型有`float`、`double`、`long double`,和整型一樣,既沒有規定每種類型占多少字節,也沒有規定采用哪種表示形式。浮點數的實現在各種平臺上差異很大,有的處理器有浮點運算單元(FPU,Floating Point Unit),稱為硬浮點(Hard-float)實現;有的處理器沒有浮點運算單元,只能做整數運算,需要用整數運算來模擬浮點運算,稱為軟浮點(Soft-float)實現。大部分平臺的浮點數實現遵循IEEE 754,`float`型通常是32位,`double`型通常是64位。 `long double`型通常是比`double`型精度更高的類型,但各平臺的實現有較大差異。在x86平臺上,大多數編譯器實現的`long double`型是80位,因為x86的浮點運算單元具有80位精度,`gcc`實現的`long double`型是12字節(96位),這是為了對齊到4字節邊界(在[第?4?節 “結構體和聯合體”](ch19s04.html#asmc.structunion)詳細討論對齊的問題),也有些編譯器實現的`long double`型和`double`型精度相同,沒有充分利用x86浮點運算單元的精度。其它體系結構的浮點運算單元的精度不同,編譯器實現也會不同,例如PowerPC上的`long double`型通常是128位。 以前我們只用到最簡單的浮點數常量,例如3.14,現在看看浮點數常量還有哪些寫法。由于浮點數在計算機中的表示是基于科學計數法的,所以浮點數常量也可以寫成科學計數法的形式,尾數和指數之間用e或E隔開,例如314e-2表示314×10<sup>-2</sup>,注意這種表示形式基數是10<sup>[[27](#ftn.id2757890)]</sup>,如果尾數的小數點左邊或右邊沒有數字則表示這一部分為零,例如3.e-1,.987等等。浮點數也可以加一個后綴,例如3.14f、.01L,浮點數的后綴和類型之間的對應關系比較簡單,沒有后綴的浮點數常量是`double`型的,有后綴f或F的浮點數常量是`float`型的,有后綴l或L的浮點數常量是`long double`型的。 * * * <sup>[[27](#id2757890)]</sup> C99引入一種新的十六進制浮點數表示,基數是2,本書不做詳細介紹。 ## 3.?類型轉換 如果有人問C語法規則中最復雜的是哪一部分,我一定會說是類型轉換。從上面兩節可以看出,有符號、無符號整數和浮點數加起來有那么多種類型,每兩種類型之間都要定義一個轉換規則,轉換規則的數量自然很龐大,更何況由于各種體系結構對于整數和浮點數的實現很不相同,很多類型轉換的情況都是C標準未做明確規定的陰暗角落。雖然我們寫代碼時不會故意去觸碰這些陰暗角落,但有時候會不小心犯錯,所以了解一些未明確規定的情況還是有必要的,可以在出錯時更容易分析錯誤原因。本節分成幾小節,首先介紹哪些情況下會發生類型轉換,會把什么類型轉成什么類型,然后介紹編譯器如何處理這樣的類型轉換。 ### 3.1.?Integer Promotion 在一個表達式中,凡是可以使用`int`或`unsigned int`類型做右值的地方也都可以使用有符號或無符號的`char`型、`short`型和Bit-field。如果原始類型的取值范圍都能用`int`型表示,則其類型被提升為`int`,如果原始類型的取值范圍用`int`型表示不了,則提升為`unsigned int`型,這稱為Integer Promotion。做Integer Promotion只影響上述幾種類型的值,對其它類型無影響。C99規定Integer Promotion適用于以下幾種情況: 1、如果一個函數的形參類型未知,例如使用了Old Style C風格的函數聲明(詳見[第?2?節 “自定義函數”](ch03s02.html#func.ourfirstfunc)),或者函數的參數列表中有...,那么調用函數時要對相應的實參做Integer Promotion,此外,相應的實參如果是`float`型的也要被提升為`double`型,這條規則稱為Default Argument Promotion。我們知道`printf`的參數列表中有`...`,除了第一個形參之外,其它形參的類型都是未知的,比如有這樣的代碼: ``` char ch = 'A'; printf("%c", ch); ``` `ch`要被提升為`int`型之后再傳給`printf`。 2、算術運算中的類型轉換。有符號或無符號的`char`型、`short`型和Bit-field在做算術運算之前首先要做Integer Promotion,然后才能參與計算。例如: ``` unsigned char c1 = 255, c2 = 2; int n = c1 + c2; ``` 計算表達式`c1 + c2`的過程其實是先把`c1`和`c2`提升為`int`型然后再相加(`unsigned char`的取值范圍是0~255,完全可以用`int`表示,所以提升為`int`就可以了,不需要提升為`unsigned int`),整個表達式的值也是`int`型,最后的結果是257。假如沒有這個提升的過程,`c1 + c2`就溢出了,溢出會得到什么結果是Undefined,在大多數平臺上會把進位截掉,得到的結果應該是1。 除了+號之外還有哪些運算符在計算之前需要做Integer Promotion呢?我們在下一小節先介紹Usual Arithmetic Conversion規則,然后再解答這個問題。 ### 3.2.?Usual Arithmetic Conversion 兩個算術類型的操作數做算術運算,比如`a + b`,如果兩邊操作數的類型不同,編譯器會自動做類型轉換,使兩邊類型相同之后才做運算,這稱為Usual Arithmetic Conversion。轉換規則如下: 1. 如果有一邊的類型是`long double`,則把另一邊也轉成`long double`。 2. 否則,如果有一邊的類型是`double`,則把另一邊也轉成`double`。 3. 否則,如果有一邊的類型是`float`,則把另一邊也轉成`float`。 4. 否則,兩邊應該都是整型,首先按上一小節講過的規則對`a`和`b`做Integer Promotion,然后如果類型仍不相同,則需要繼續轉換。首先我們規定`char`、`short`、`int`、`long`、`long long`的轉換級別(Integer Conversion Rank)一個比一個高,同一類型的有符號和無符號數具有相同的Rank。轉換規則如下: 1. 如果兩邊都是有符號數,或者都是無符號數,那么較低Rank的類型轉換成較高Rank的類型。例如`unsigned int`和`unsigned long`做算術運算時都轉成`unsigned long`。 2. 否則,如果一邊是無符號數另一邊是有符號數,無符號數的Rank不低于有符號數的Rank,則把有符號數轉成另一邊的無符號類型。例如`unsigned long`和`int`做算術運算時都轉成`unsigned long`,`unsigned long`和`long`做算術運算時也都轉成`unsigned long`。 3. 剩下的情況是:一邊有符號另一邊無符號,并且無符號數的Rank低于有符號數的Rank。這時又分為兩種情況,如果這個有符號數類型能夠覆蓋這個無符號數類型的取值范圍,則把無符號數轉成另一邊的有符號類型。例如遵循LP64的平臺上`unsigned int`和`long`在做算術運算時都轉成`long`。 4. 否則,也就是這個有符號數類型不足以覆蓋這個無符號數類型的取值范圍,則把兩邊都轉成有符號數的Rank對應的無符號類型。例如在遵循ILP32的平臺上`unsigned int`和`long`在做算術運算時都轉成`unsigned long`。 可見有符號和無符號整數的轉換規則是十分復雜的,雖然這是有明確規定的,不屬于陰暗角落,但為了程序的可讀性不應該依賴這些規則來寫代碼。我講這些規則,不是為了讓你用,而是為了讓你了解有符號數和無符號數混用會非常麻煩,從而避免觸及這些規則,并且在程序出錯時記得往這上面找原因。所以這些規則不需要牢記,但要知道有這么回事,以便在用到的時候能找到我書上的這一段。 到目前為止我們學過的+ - * / % &gt; &lt; &gt;= &lt;= == !=運算符都需要做Usual Arithmetic Conversion,因為都要求兩邊操作數的類型一致,在下一章會介紹幾種新的運算符也需要做Usual Arithmetic Conversion。單目運算符+ - ~只有一個操作數,移位運算符&lt;&lt; &gt;&gt;兩邊的操作數類型不要求一致,這些運算不需要做Usual Arithmetic Conversion,但也需要做Integer Promotion,運算符~ &lt;&lt; &gt;&gt;將在下一章介紹。 ### 3.3.?由賦值產生的類型轉換 如果賦值或初始化時等號兩邊的類型不相同,則編譯器會把等號右邊的類型轉換成等號左邊的類型再做賦值。例如`int c = 3.14;`,編譯器會把右邊的`double`型轉成`int`型再賦給變量`c`。 我們知道,函數調用傳參的過程相當于定義形參并且用實參對其做初始化,函數返回的過程相當于定義一個臨時變量并且用`return`的表達式對其做初始化,所以由賦值產生的類型轉換也適用于這兩種情況。例如一個函數的原型是`int foo(int, int);`,則調用`foo(3.1, 4.2)`時會自動把兩個`double`型的實參轉成`int`型賦給形參,如果這個函數定義中有返回語句`return 1.2;`,則返回值`1.2`會自動轉成`int`型再返回。 在函數調用和返回過程中發生的類型轉換往往容易被忽視,因為函數原型和函數調用并沒有寫在一起。例如`char c = getchar();`,看到這一句往往會想當然地認為`getchar`的返回值是`char`型,而事實上`getchar`的返回值是`int`型,這樣賦值會引起類型轉換,可能產生Bug,我們在[第?2.5?節 “以字節為單位的I/O函數”](ch25s02.html#stdlib.byteio)詳細討論這個問題。 ### 3.4.?強制類型轉換 以上三種情況通稱為隱式類型轉換(Implicit Conversion,或者叫Coercion),編譯器根據它自己的一套規則將一種類型自動轉換成另一種類型。除此之外,程序員也可以通過類型轉換運算符(Cast Operator)自己規定某個表達式要轉換成何種類型,這稱為顯式類型轉換(Explicit Conversion)或強制類型轉換(Type Cast)。例如計算表達式`(double)3 + i`,首先將整數3強制轉換成`double`型(值為3.0),然后和整型變量`i`相加,這時適用Usual Arithmetic Conversion規則,首先把`i`也轉成`double`型,然后兩者相加,最后整個表達式也是`double`型的。這里的`(double)`就是一個類型轉換運算符,這種運算符由一個類型名套()括號組成,屬于單目運算符,后面的3是這個運算符的操作數。注意操作數的類型必須是標量類型,轉換之后的類型必須是標量類型或者`void`型。 ### 3.5.?編譯器如何處理類型轉換 以上幾小節介紹了哪些情況下會發生類型轉換,并且明確了每種情況下會把什么類型轉成什么類型,本節介紹編譯器如何處理任意兩種類型之間的轉換。現在要把一個M位的類型(值為X)轉換成一個N位的類型,所有可能的情況如下表所示。 **表?15.3.?如何做類型轉換** | 待轉換的類型 | M &gt; N的情況 | M == N的情況 | M &lt; N的情況 | | --- | --- | --- | --- | | signed integer to signed integer | 如果X在目標類型的取值范圍內則值不變,否則Implementation-defined | 值不變 | 值不變 | | unsigned integer to signed integer | 如果X在目標類型的取值范圍內則值不變,否則Implementation-defined | 如果X在目標類型的取值范圍內則值不變,否則Implementation-defined | 值不變 | | signed integer to unsigned integer | X % 2<sup>N</sup> | X % 2<sup>N</sup> | X % 2<sup>N</sup> | | unsigned integer to unsigned integer | X % 2<sup>N</sup> | 值不變 | 值不變 | | floating-point to signed or unsigned integer | Truncate toward Zero,如果X的整數部分超出目標類型的取值范圍則Undefined | | signed or unsigned integer to floating-point | 如果X在目標類型的取值范圍內則值不變,但有可能損失精度,如果X超出目標類型的取值范圍則Undefined | | floating-point to floating-point | 如果X在目標類型的取值范圍內則值不變,但有可能損失精度,如果X超出目標類型的取值范圍則Undefined | 值不變 | 值不變 | 注意上表中的“X % 2<sup>N</sup>”,我想表達的意思是“把X加上或者減去2<sup>N</sup>的整數倍,使結果落入[0, 2<sup>N</sup>-1]的范圍內”,當X是負數時運算結果也得是正數,即運算結果和除數同號而不是和被除數同號,這不同于C語言%運算的定義。寫程序時不要故意用上表中的規則,尤其不要觸碰Implementation-defined和Undefined的情況,但程序出錯時可以借助上表分析錯誤原因。 下面舉幾個例子說明上表的用法。比如把`double`型轉換成`short`型,對應表中的“floating-point to signed or unsigned integer”,如果原值在(-32769.0, 32768.0)之間則截掉小數部分得到轉換結果,否則產生溢出,結果是Undefined,例如對于`short s = 32768.4;`這個語句`gcc`會報警告。 比如把`int`型轉換成`unsigned short`型,對應表中的“signed integer to unsigned integer”,如果原值是正的,則把它除以2<sup>16</sup>取模,其實就是取它的低16位,如果原值是負的,則加上2<sup>16</sup>的整數倍,使結果落在[0, 65535]之間。 比如把`int`類型轉換成`short`類型,對應表中的“signed integer to signed integer”,如果原值在[-32768, 32767]之間則值不變,否則產生溢出,結果是Implementation-defined,例如對于`short s = -32769;`這個語句`gcc`會報警告。 最后一個例子,把`short`型轉換成`int`型,對應表中的“signed integer to signed integer”,轉換之后應該值不變。那怎么維持值不變呢?是不是在高位補16個0就行了呢?如果原值是-1,十六進制表示就是ffff,要轉成`int`型的-1需要變成ffffffff,因此需要在高位補16個1而不是16個0。換句話說,要維持值不變,在高位補1還是補0取決于原來的符號位,這稱為符號擴展(Sign Extension)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看