2.1 C語言基本數據類型程序員
在計算機術語中,把⼆進制數中的某⼀位數又稱爲⼀個⽐特(bit)。⽐特這個單位對於計算機⽽⾔,在度量上是最⼩的單位。除了⽐特以外,還有字節(byte)這個術語。⼀個字節由8個⽐特構成。在某些單⽚機架構下還引⼊了半字節(nybble或nibble)這個概念,表⽰4個⽐特。而後,還有字(word)這個術語。字在不一樣計算機架構下表⽰的含義不一樣。在x86架構下,⼀個字爲2個字節;⽽在ARM等衆多32位RISC體系結構下,⼀個字表⽰爲4個字節。隨着計算機帶寬的提高,能被處理器⼀次處理的數據寬度也不斷提高,所以出現了雙字(double word)、四字(quad word)、⼋字(octa word)等概念。雙字的寬度爲2個字,四字寬度爲4個字,因此它們在不一樣處理器體系結構下所佔⽤的字節個數也會不一樣。編程
2.1.1 整數數組
咱們⽇常⽤的整數都是⼗進制數(Decimal),也就是咱們一般所說的逢⼗進⼀。由於咱們⼈類有⼗根⼿指,因此⾃然⽽然地會想到採⽤⼗進制的計數和計算⽅式。然⽽,如今⼏乎全部計算機都採⽤⼆進制數(Binary)編碼⽅式,因此咱們⽇常所⽤到的整數若是要⽤計算機來表⽰的話,須要表⽰成⼆進制的⽅式。至於二進制、八進制、十六進制的細節就不講了,這些在計算機原理或彙編語言教材裏都會講。卻是Big Endian(大端)和Little Endian(小端)須要瞭解一下,這裏也不講,你們在網上能夠找到。習慣上,⽤0或0o打頭的數表⽰⼋進制數,0x打頭的數表⽰⼗六進制數。⽐如,012三、0777表⽰⼋進制數;0x123,0xABCD表⽰⼗六進制數。架構
在計算機中,整數又分爲無符號整數和有符號整數。像short、int、long、long long,都是有符號整數(有負數)類型,而要表示無符號整數(無負數)類型,只需在前面加上unsigned前綴便可。bool和char比較特別,它們本質上也是整數類型,但它們必須無符號。編輯器
C語⾔標準中沒有明確規定每⼀種整數類型所佔⽤的字節數,這些全都是由C語⾔的實現來定義的,可是C語⾔標準給了若⼲約束,因此C語⾔實現應該⾄少能滿⾜這些約束。爲了⽅便敘述,咱們這⾥仍然根據主流桌⾯端編譯器(GCC、Clang)以及主流32位與64位處理器環境的實現進⾏講解。函數
2.1.1.1 short類型ui
short類型(標準表達爲signed short int類型,其中signed與int都可省略)咱們⼀般稱之爲短整型。在咱們一般的32位及64位系統下佔⽤2個字節(即16位),其最⼩值爲-215(即0x8000),最⼤值爲215-1(即0x7FFF)。在C語⾔執⾏環境下,其最⼤、最⼩值分別定義爲<limits.h>頭⽂件中的SHRT_MAX和SHRT_MIN。short類型所對應的⽆符號類型爲unsigned short(標準表達爲unsigned short int,其中int可省)。它一般在32位及64位系統下佔2個字節,最⼩值爲0,最⼤值爲216-1(即0xFFFF)。在C語⾔執⾏環境中,其最⼤值定義爲<limits.h>頭⽂件中的USHRT_MAX。編碼
short類型與unsigned short類型沒有特別對應的整數字⾯量,它們可直接⽤int與unsigned int相應的整數字⾯量進⾏賦值。指針
2.1.1.2 int類型code
⽤關鍵字int聲明的⼀個整數對象具備int類型。在具體的C語⾔執⾏環境中,int數據的最⼩值與最⼤值分別定義爲<limits.h>頭⽂件中的INT_MIN和INT_MAX。在咱們常⽤的32位與64位環境中,int默認爲是帶符號的(至關於signed int),佔⽤4個字節(即32位),其最⼩值爲-231(即0x80000000),最⼤值爲231-1(即0x7FFFFFFF)。int所對應的⽆符號類型是unsigned int,一般在32位與64位環境下也佔⽤4個字節,最⼩值爲0,最⼤值爲232-1(即0xFFFFFFFF)。在具體C語⾔執⾏環境中的最⼤值定義爲<limits.h>頭⽂件中的UINT_MAX。
int類型對應的整數字⾯量可直接按照⾃然⽅式書寫,⽐如0、-12八、12七、+2233等都默認表⽰爲int類型。此外,整數字⾯量能夠分別使⽤⼋進制、⼗進制以及⼗六進制的⽅式進⾏表達。⼋進制的整數字⾯量表達⽅式爲以0打頭,⽐如:0一、02三、-0477這些都是屬於⼋進制整數字⾯量。⽽⼗六進制整數字⾯量則是以0x或0X打頭,⽐如:0x12三、-0x004五、0xabcdef這些都是有效的⼗六進制整數字⾯量。⽽其餘沒有任何前綴的整數字⾯量都表⽰爲⼗進制整數。若是想要表達⼀個unsigned int類型的整數字⾯量,可在⼀般整數字⾯量後直接添加字母u或U。本書習慣上使⽤⼤寫的U。⽐如0U、01U、-128U、2048U、+2233U等都屬於unsigned int類型。固然,即使字⾯量後⾯不加U後綴,這些數也能賦值給unsigned int類型的對象,由於它們會被編譯器進⾏默認轉換。此外,當咱們要聲明⼀個unsigned int類型的對象時,int能夠省略。⽐如,unsigned a=0;,其中對象a的類型即爲unsigned int類型,=是⼀個賦值操做符(assignment operators),將其右操做數0賦值給左操做數a。
2.1.1.3 long類型
long類型(標準表達爲signed long int類型,其中signed與int都可省略)咱們⼀般稱之爲長整型。在咱們一般的32位環境下long類型佔⽤4個字節(即32位),⽽在64位系統下,當前⼏個主流桌⾯編譯器就有所區別了。MSVC與VS-Clang仍然爲4個字節,⽽GCC與Clang則是8個字節(即64位)。long類型所對應的⽆符號類型爲unsigned long(標準表達爲unsigned long int,int可省),咱們⼀般稱之爲⽆符號長整型,它的字節長度與long類型⼀致。在C語⾔執⾏環境中,long類型的最⼩值與最⼤值分別定義爲<limits.h>頭⽂件中的LONG_MIN與LONG_MAX。unsigned long類型的最⼤值定義爲<limits.h>頭⽂件中的ULONG_MAX,其最⼩值爲0。
long類型對應的整數字⾯量是在int整數字⾯量後⾯加上英⽂字母l或L,本書都⽤⼤寫字母L做爲後綴。unsigned long對應的整數字⾯量是在unsigned int字⾯量後⾯加上字母l或L,一般都是以UL做爲後綴。⼀般狀況下,咱們直接⽤int字⾯量賦值給long類型的變量也不會有問題,可是當咱們要表達⼀個超出int範圍的整數時,咱們就得加上後綴L,不然數據可能會被截斷。不過這⾥,不一樣的編譯器會有不一樣⾏爲。
由於long和unsigned long在不一樣環境下字節長度不一樣,因此咱們在定義⼀個整數對象時應當儘可能避免使⽤long類型,除⾮涉及系統相關的⼀些屬性。⽐如C語⾔標準庫中將獲取⽂件當前位置(ftell)等函數的返回類型做爲long類型。但對於⼀般應⽤程序⽽⾔,咱們須要慎⽤long類型。
2.1.1.4 long long類型
C語⾔標準對long long(標準表達爲signed long long int,其中signed與int可省)類型提得很少,僅僅闡述了long long類型的精度⾄少爲long int類型的精度。不過在當前⼏⼤主流桌⾯編譯器中,⽆論是32位系統仍是64位系統,long long的寬度均爲8個字節(即64位)。其最⼩值爲-263,最⼤值爲263-1。long long對應的⽆符號類型爲unsigned long long(標準表達爲unsigned long long int,int可省),一樣也是8字節的寬度,最⼩值爲0,最⼤值爲264-1。在C語⾔執⾏環境下,long long的最⼩值與最⼤值分別定義爲<limits.h>頭⽂件中的LLONG_MIN與LLONG_MAX。unsigned long long類型的最⼤值定義爲<limits.h>頭⽂件中的ULLONG_MAX。
long long對應的整數字⾯量表⽰爲int整數字⾯量後加後綴ll或LL,一般採⽤LL後綴。unsigned long long對應的整數字⾯量表⽰爲unsigned int整數字⾯量後⾯加後綴ll或LL,本書採⽤ULL做爲後綴。
2.1.1.5 bool(布爾)類型
在計算機編程語⾔中,布爾類型的對象是⼀個⼆值數據對象。布爾類型⽤於表達真假邏輯關係,⼀般⽤true表⽰真,false表⽰假。產⽣布爾值的表達式稱爲邏輯表達式或關係表達式(⽐如,⼤於、等於、⼩於、不等於等關係操做的結果)。在C11標準中,布爾類型⽤關鍵字_Bool聲明,並說明布爾類型只要可以存放0和1值就⾏,也就是⾄少爲1個⽐特。因此如今⼤部分對_Bool的C語⾔實現都將它做爲1個字節的寬度。此外,_Bool類型不能⽤signed和unsigned來修飾。
在C語⾔剛被建立的時候,它並不具有「布爾類型」這個概念,⽽僅僅⽤0(浮點數則爲0.0)與對象⽐較來斷定真假。若是對象的值等於零,那麼表⽰「假」,不然表⽰「真」。因此,即使從C99開始引⼊了_Bool布爾類型,以前的這個約定依然沿⽤。爲了能與C++兼容,C語⾔從C99標準開始就引⼊了<stdbool.h>頭⽂件,⾥⾯⽤bool這個宏來定義_Bool,⽤true定義爲1,false定義爲0。bool、true以及false都不屬於C語⾔中的關鍵字,它們僅屬於標準庫中定義的類型和常量。在C11標準的語⾔核⼼中,依然只定義了_Bool這個關鍵字表⽰布爾類型,⽽沒有定義真值和假值的字⾯量。因此,咱們在使⽤布爾類型的對象時,最好引⼊<stdbool.h>頭⽂件,而後⽤bool定義布爾類型對象,⽤true表⽰真值常量,false表⽰假值常量。
2.1.1.6 char(字符)類型
C語⾔中⽤關鍵字char來聲明⼀個字符類型。C11標準闡明瞭⼀個char類型的對象必須⾄少能存放基本執⾏字符集,而且若是⼀個基本執⾏字符存放在⼀個char類型的對象中的話,那麼該char類型的對象的值必須保證爲⾮負整數。因此,一般C語⾔的實現都會將char類型的寬度設置爲⼀個字節,這樣正好⾄少能存放ASCII碼字符集。
這⾥各位須要當⼼的是,有些編譯器會默認將char類型設定爲⽆符號的,即char類型的整數是⼀個⽆符號的8位整數。因此,咱們若是要⽤char類型定義⼀個帶符號的8位整數,須要顯式地使⽤signed char,這⾥signed不該該被省略。若是要聲明⼀個⽆符號8位整數,則使⽤unsigned char。C11標準明確指出,char、signed char與unsigned char統稱爲字符類型,但三者在類型上是不兼容的,儘管char在數值表⽰範圍上可能與unsigned char相同,或與signed char相同。所以如前所述,咱們在編寫程序的時候,⽤signed char來指定8位帶符號整數;unsigned char來指定⽆符號8位整數;char⽤於指定⼀個基本字符對象。signed char的最⼤、最⼩值分別定義爲<limits.h>中的SCHAR_MAX與SCHAR_MIN。unsigned char的最⼤值定義爲<limits.h>中的UCHAR_MAX,最⼩值爲0。char的最⼤、最⼩值分別定義爲<limits.h>中的CHAR_MAX和CHAR_MIN。
8位帶符號與⽆符號的整數字⾯量沒有特定的字⾯量表⽰⽅式,直接⽤int與unsigned int類型的整數字⾯量便可。⽽字符字⾯量則是⽤單引號,⾥⾯包含⼀個或多個字符。⽐如'a'、'123'都是有效的字符字⾯量。C11標準明確規定,⼀個字符字⾯量具備int類型,若是將⼀個字符字⾯量賦值給⼀個char類型的對象,那麼將該字符字⾯量的最低有效位賦值給它,⽐如在咱們一般的執⾏環境中就是將字符字⾯量的最低字節賦值給char類型的對象。
在C語⾔中,不是全部的字符字⾯量都能回顯在⽂本編輯器中,另外還有⼀些字符具備特殊做⽤,⽐如換⾏、製表符等,⽽且像單引號本⾝也表⽰⼀個字符字⾯量的開頭或結尾,因此對於這些特殊字符,咱們經過使⽤轉義字符的⽅式來表⽰它們。下⾯列舉⼀下C語⾔中的轉義字符。
(1)單引號:⽤\'。
(2)雙引號:⽤\"。
(3)問號:⽤\?,不過⼀般咱們能夠直接使⽤'?',⽆需使⽤此轉義字符。
(4)倒斜槓\:⽤\\。
(5)⽤⼋進制編碼表⽰的⼀個字符:\後⾯緊跟1到3個⼋進制數。⽐如:\七、\十二、\123等。
(6)⽤⼗六進制編碼表⽰⼀個字符:\x後⾯跟⼀個⼗六進制數。⽐如:\x0a、\x30等。
注意:\x後⾯所跟的全部能有效表⽰爲⼗六進制數的字符(即0~9,⼤寫字母A~F以及⼩寫字母a~f)都做爲當前單個⼗六進制編碼的字符,直到遇到⽆法有效表⽰⼗六進制數的字符爲⽌。另外,若是\x後⾯不是緊跟⼀個有效的⼗六進制字符,那麼編譯器將會報錯。因此\x後必須⾄少跟⼀個有效的⼗六進制字符。
(7)\a:表⽰報警。該字符會產⽣⼀個可聽到的或可見到的警報,但不改變當前的遊標位置。
(8)\b:表⽰回退。該字符將當前遊標移動到當前⾏的前⼀個位置。
(9)\f:表⽰換頁。該字符將當前遊標移動到下⼀個邏輯頁的初始位置。
(10)\n:表⽰換⾏。該字符將當前遊標移動到下⼀⾏的初始位置。
(11)\r:表⽰回車。該字符將當前遊標移動到當前⾏的初始位置。
(12)\t:表⽰⽔平製表符。該字符將當前遊標移動到當前⾏的下⼀個⽔平表格單元位置。
(13)\v:表⽰垂直製表符。該字符將當前遊標移動到下⼀垂直表格單元位置的初始位置。
(14)\0:表⽰空。值爲0的字符在C語⾔中⼀般⽤於字符串的結束符。C語⾔標準庫中的不少庫函數都以\0字符做爲⼀個字符串末尾的判斷依據。
2.1.1.7 寬字符以及Unicode字符類型
從C99標準中引⼊了wchar_t類型來表⽰⼀個多字節字符。wchar_t並非C語⾔的⼀個關鍵字,⽽是定義在<stddef.h>頭⽂件中的⼀個宏類型。wchar_t類型在不一樣環境,其長度也可能不⼀樣,C語⾔標準沒有規定它必須佔⽤多少字節。當前編譯器⼀般將wchar_t定義爲4個字節的寬度,有些⽼的編譯器可能爲2個字節。寬字符的字⾯量爲⼀般字符字⾯量前加⼤寫字母L前綴,這⾥各位要注意,必須是⼤寫字母,不能是⼩寫的。⽐如,L'a'、L'你'等都屬於wchar_t類型的寬字符字⾯量。寬字符在C語⾔中的定義⽐較模糊,它主要根據當前系統的語⾔環境設置,多是UTF-16編碼、GB23十二、拉丁系編碼格式等。寬字符在不一樣語⾔環境下,其相應的所顯⽰出來的字樣均可能會不一樣。由此,C語⾔標準組織在C11標準中引⼊了Unicode字符類型。
C11中主要引⼊了UTF-8字符串、UTF-16字符以及字符串類型和UTF-32字符及字符串類型。正如在2.6節所描述的,UTF-8編碼的長度範圍爲1~4個字節,因此在C語⾔中能夠直接⽤char類型來表⽰當前⼀個UTF-8編碼字符的⼀個字節,它已經涵蓋了基本的ASCII碼。若是要表⽰中⽂、⽇⽂等UTF-8字符的話,則須要使⽤字節數組。C11中,引⼊了新的頭⽂件<uchar.h>,其中定義了UTF-16字符類型----char16_t以及UTF-32字符類型----char32_t。不過C語⾔標準委員會作得⾮常靈活,在標準中提到,當C語⾔編譯器預先定義了__STDC_UTF_16__這個宏時,char16_t才保證被⽤做爲UTF-16編碼;當預先定義了__STDC_UTF_32__這個宏時,char32_t才保證被⽤做爲UTF-32編碼;不然char16_t和char32_t可能會留做其餘字符編碼類型使⽤。在C11中,UTF-16的字⾯量是在普通字符字⾯量前加⼩寫字母u,⽐如u'a'、u'我'等都是UTF-16字符字⾯量;UTF-32的字⾯量是在普通字符字⾯量前加⼤寫字母U,⽐如U'b'、U'好'等都是UTF-32字符字⾯量。一樣,C11標準沒有明確提到char16_t與char32_t的寬度,如今編譯器⼀般將char16_t定義爲unsigned short類型,佔2個字節;將char32_t定義爲unsignedint類型,佔4個字節。
須要注意的是,頭⽂件<uchar.h>還沒有包含在macOS等部分Unix系統中,因此咱們⽤unsigned short來代替char16_t,或者咱們能夠⽤代碼清單5-8的代碼⾃⼰建⼀個uchar.h頭⽂件。⽽在Windows系統中卻是已經包含了,各位能夠在VS-Clang、MinGW等編譯器中直接使⽤。不過⽆論在哪一種系統環境下,GCC和Clang編譯器對UTF-16以及UTF-32字符的字⾯量都已經⽀持。
2.1.1.8 size_t與ptrdiff_t類型
size_t在以前的標準中主要⽤於sizeof操做符的返回類型。C11標準引⼊了_Alignof操做符以後,它的返回類型也是size_t。size_t定義在<stddef.h>頭⽂件中。一般咱們使⽤size_t做爲⼀個指針(或地址)轉換⼀個整數的⽅式,它⼀般是⽆符號的。在MSVC與MS-Clang編譯器中,32位環境下被定義爲unsigned int,64位環境下被定義爲unsigned long long。在GCC和Clang編譯器中,⽆論是32位仍是64位環境,size_t都被定義爲unsigned long,由於unsigned long在GCC和Clang中,在32位環境下是32位的,在64位環境下是64位的。這麼⼀來,⽆論是哪一個編譯器,size_t數據類型都能存放當前系統環境下的⼀個地址長度。咱們將在5.5節詳細講解sizeof操做符。
ptrdiff_t類型⽤於兩個指針相減後的結果類型,它是帶符號的,在<stddef.h>頭⽂件中定義。在一般C語⾔實現中,它的寬度與size_t相同,僅有的區別是ptrdiff_t是帶符號的,⽽size_t則每每是⽆符號的。
2.1.2 C語言中的標準整數類型
在前⾯所講述的整數類型中,咱們已經提起過像int、long之類的類型在不一樣的編譯運⾏環境下可能會有不一樣字節長度,尤爲是long類型。爲了能使代碼適應更⼴泛的編譯執⾏環境,咱們在編寫C語⾔代碼時能夠考慮使⽤從C99標準就已經引⼊的標準整數類型。標準整數類型⼀般被定義在<stdint.h>頭⽂件中,主要包含如下⼏類。
(1)固定寬度的整數類型:當前標準可以⽀持int8_t、uint8_t、int16_t、uint16_t、int32_t、uint32_t、int64_t、uint64_t這些常⽤的類型。使⽤這些類型以後,咱們就⽆需糾結signed char的字節寬度、short的字節寬度、long的字節寬度等都是多少,由於這些類型從字⾯上就已經代表了它們分別佔多少字節。⽐如int8_t的寬度就是1個字節(8⽐特);int32_t則是4個字節(32⽐特)。此外,以int做爲前綴的類型表⽰是帶符號的類型;以uint爲前綴的類型表⽰⽆符號類型。因此,咱們從此寫C語⾔代碼時應當優先考慮這些標準整數類型。除了這些常⽤的標準整數類型外,C11標準還定義了其餘固定寬度的標準類型,不過這些類型都是可選的,C語⾔實現不必⼀定⽀持,⽐如:int24_t、uint24_t、int40_t、uint40_t、int48_t、uint48_t、int56_t、uint56_t。
(2)最⼩寬度整數類型:這些整數類型表⽰⾄少須要滿⾜所指定的⽐特位數,但容許佔⽤更多的⽐特位。這些類型主要有:int_least8_t、uint_least8_t、int_least16_t、uint_least16_t、int_least32_t、uint_least32_t、int_least64_t、uint_least64_t。此外,還有可選的2四、40、4八、56⽐特寬度的最⼩寬度整數類型。
(3)最快最⼩寬度的整數類型:這些整數類型每每⽤於快速計算。它們與最⼩寬度整數類型相似,⾄少須要滿⾜所指定的⽐特位數。不過與最⼩寬度整數類型不一樣的是,它們每每具備更快速的計算速度。⽐如說,有些硬件(⽐如AMD的基於GCN架構的GPU)具備24位整數的快速乘法計算,那麼當程序員使⽤了int_fast24_t時,則能暗⽰編譯器⽣成利⽤這種快速乘法的指令。這些類型主要包括:int_fast8_t、uint_fast8_t、int_fast16_t、uint_fast16_t、int_fast32_t、uint_fast32_t、int_fast64_t、uint_fast64_t。另外,還有可選的2四、40、4八、56⽐特寬度。
(4)能存放對象指針的整數類型:該類型有intptr_t與uintptr_t兩個。前者是帶符號的,後者是⽆符號的。這個類型⽤於將⼀個對象的地址或是⼀個指針對象的值⽤⼀個整數存放起來。
(5)最⼤寬度的整數類型:這種類型表⽰當前C語⾔實現能容納全部整數的最⼤整數類型,有intmax_t和uintmax_t這兩個。
2.1.3 浮點數
浮點數與數學中實數的概念差很少。2.7五、3.16E七、7.00和2e-8都是浮點數。注意,在一個值後面加上一個小數點,該值就成爲一個浮點值。因此,7是整數,7.00是浮點數。顯然,書寫浮點數有多種形式。
這裏關鍵要理解浮點數和整數的儲存方案不一樣。計算機把浮點數分紅小數部分和指數部分來表示,並且分開儲存這兩部分。所以,雖然7.00和7在數值上相同,可是它們的儲存方式不一樣。在十進制下,能夠把7.0寫成0.7E1。這裏,0.7是小數部分,1是指數部分。
對於一些算術運算(如,兩個很大的數相減),比較整數而言,浮點數損失的精度更多。
當前主流處理器⼀般都能⽀持32位的單精度浮點數與64位的雙精度浮點數的表⽰和計算,而且能遵循IEEE754-1985⼯業標準。如今此標準最新的版本是2008,其中增長了對16位半精度浮點數以及128位四精度浮點數的描述。C語⾔標準引⼊了⼀個浮點模型,可⽤來表達任意精度的浮點數,儘管當前主流C語⾔編譯器還沒有很好地⽀持半精度浮點數與四精度浮點數的表⽰和計算。
當前在C語⾔中有3種實數浮點類型,分別爲float、double與longdouble。C語⾔標準僅僅規定了float類型的精度是double類型精度的⼦集;double類型精度是long double精度的⼦集。在⼀般C語⾔實現中,將float類型設定爲32位單精度浮點型,並採⽤IEEE754中的規格化浮點數表⽰⽅法;將double類型設定爲64位雙精度浮點型,並採⽤IEEE754中的規格化浮點數表⽰⽅法;long double在x86架構處理器下表⽰擴展雙精度浮點(80位浮點數,⼀般佔⽤16個字節),這是Intel⾃⼰擴展出來的浮點數格式。⽽在ARM等其餘處理器架構下,long double可能與double類型⼀樣,表⽰雙精度浮點類型,但寬度仍然多是16字節,⽽不是8字節。在⼀些GPU、DSP或嵌⼊式處理器中,浮點類型可能⽀持部分IEEE754標準,甚⾄使⽤其餘表⽰法也有可能,因此各位使⽤時須要注意。但在⼤部分桌⾯環境以及智能設備上都基本能夠滿⾜IEEE754標準。
在C語⾔中,浮點數字⾯量的表達⽅式⾮常豐富,最基本的就是如0.一、-100.05等這種正常的⼗進制浮點在數學上的表⽰⽅法。在⼗進制浮點數後⾯添加f或F後綴,表⽰該字⾯量是float類型浮點數;不添加任何後綴表⽰double類型浮點字⾯量;添加l或L後綴表⽰long double類型的浮點字⾯量。另外,若是浮點數的⼩數部分爲0,那麼咱們也能夠寫爲:10.、-5.等形式,10.至關於10.0。一樣,若是整數部分爲0,那麼咱們也能夠寫做爲.2五、.1001等形式,.25至關於0.25。
此外,C語⾔還引⼊了對浮點數的科學計數法的表⽰。這裏不細講。
2.2 數據精度與類型轉換
⼏乎全部計算機編程語⾔都會涉及數據精度以及類型轉換的問題。⼀般來講,⼀個類型所佔⽤的字節個數越多(即寬度越⼤),其精度也就越⾼。在C語⾔中,將整數精度等級稱爲「整數轉換等級」。
C11標準提出如下約定:
(1)任意兩個不一樣類型的帶符號整數不會具備相同等級,即便它們所表⽰的值⼀模⼀樣。⽐如在32位環境中,⼀般int類型與long類型在整數數值上表現是徹底⼀樣的,都表⽰32位帶符號整數,但long的等級仍然⾼於int的等級。
(2)更⾼精度的帶符號整數類型的轉換等級應該⾼於較低精度的帶符號整數類型的等級。
(3)⼀個⽆符號整數類型的轉換等級與其相應的帶符號整數類型的等級相同。⽐如,short類型的轉換等級與unsigned short類型的⼀樣。
(4)具體的帶符號整數的轉換等級以下(從⾼到低):long long int>long int>int>short int>signed char。
(5)char的等級應該與signed char和unsigned char相同。
(6)size_t與ptrdiff_t的等級不該該⾼於signed long int,除⾮C語⾔實現須要⽀持更⼤的對象。就這⼀點⽽⾔,GCC與Clang編譯器在64位執⾏模式下將size_t的實現定義爲unsigned long,這是合乎標準的;⽽MSVC與VS-Clang卻將size_t定義爲了unsigned long long,這顯然沒有遵照標準的⼀般規定。
(7)_Bool類型(即布爾類型)的等級在全部標準整數類型中是最低的。
2.2.1 整數晉升
對於⼀個整數類型,若是它的精度等級在計算過程當中從低轉換到⾼,那麼這個過程稱爲「整數晉升」,它是⼀個隱式轉換,⽆需程序員寫投射操做符進⾏類型轉換。在C語⾔標準中,之因此稱爲整數晉升是由於低轉換等級提高到⾼轉換等級類型,在整數數據上不會有任何變化,⽽僅僅是類型變爲更⾼等級了。
2.2.2 帶符號與⽆符號整數之間的轉換
當⼀個帶符號(⽆符號)整數類型要轉換爲另⼀個⽆符號(帶符號)整數類型時,若是轉換⽬標類型有⾜夠⼤的精度來容納原始類型,那麼轉換後的值是保持不變的。
當⼀個原始整數類型要轉換爲⽆符號整數類型時,若是⽬標⽆符號整數類型⽆法容納原始整數,那麼,原始整數值經過不斷地加(或減)⽬標類型最⼤能表⽰的值再加1,直到該值剛好能落在⽬標⽆符號整數可表⽰範圍內。⽐如,⼀個-129的short類型要轉換爲unsigned char類型,那麼將-129加上unsigned char最⼤能表⽰的值255再加1,即-129+(255+1)=127。127正好在unsigned char所表⽰的範圍內,加法停⽌,127就是最終轉換到unsigned char類型的值。固然,這個是正式的數學上的表達⽅式。在實際
應⽤中,咱們⽆需那麼⿇煩去計算,直接將超出⽬標⽆符號類型的位全都捨去便可。⽐如,-129的16位⼆進制數爲1111111101111111,若是將它轉換爲8位⽆符號類型,那麼咱們直接將⾼8位捨去,取其低8位做爲⽬標⽆符號8位整數類型便可,因此轉換後的結果就是01111111,即0x7F,對應於⼗進制數127。這是⾼精度原始整數轉爲低精度⽆符號整數的狀況。若是是⼀個低精度的帶符號整型轉換爲更⾼精度的⽆符號整數類型,那麼須要先判斷原始低精度整數的符號位,若是是0(表⽰⾮負整數),則⽤0填充到⾼精度⽆符號整數;若是是1(表⽰負整數),那麼⽤1填充到⾼精度⽆符號整數。⽐如,帶符號8位整數-1(⼆進制數爲11111111)將它轉爲⽆符號16位整數,結果爲1111111111111111,即65535。
當⼀個原始類型要轉爲帶符號整數類型時,若是⽬標帶符號整數類型⽆法容納原始整數,那麼結果是由實現定義的,C語⾔實現也可選擇發出異常信號。⽽如今主流C語⾔編譯器(⽐如MSVC、GCC和Clang)對⽬標類型爲帶符號整數類型的轉換與⽬標爲⽆符號整數類型相似。若是是⾼精度整型轉低精度帶符號整型,那麼直接作⼆進制數的⾼位截斷便可。若是是低精度的⽆符號整型轉⾼精度帶符號整型,那麼⾼位直接⽤0擴充。⽐如,⽆符號8位整數255轉爲帶符號16位整數,仍然是255;⽆符號16位整數1023轉爲帶符號8位整數,即0000001111111111轉爲帶符號8位整數,直接截斷⾼8位,保留低8位,獲得11111111,結果爲-1。
綜上所述,對於當前主流C語⾔編譯器⽽⾔,整數類型轉換主要看源操做數類型,若是源操做數是⽆符號整數類型,那麼作低精度到⾼精度的整數類型轉換時採⽤⾼位填0的⽅式;若是源操做數是帶符號整數類型,那麼作低精度到⾼精度的整數類型轉換時採⽤的是⾼位填符號位的⽅式。⽽對於⾼精度到低精度的整數轉換,⽆論源操做數是帶符號整數仍是⽆符號整數,都是採⽤⾼位截斷的⽅式。
在有些較⽼版本的編譯器或者把編譯器警告等級調得較⾼的狀況下,⾼轉換等級的整數類型轉換爲低轉換等級的整數可能會出現警告,此時咱們能夠⽤投射操做符(cast operator)作顯式的類型轉換來規避這些警告。不過,C11標準提到的是,投射操做符主要⽤於涉及指針的類型轉換,對於基本數據類型,可⽤也可不⽤。
投射操做符⾮常簡單,就是⽤圓括號將某個類型包圍住。⽐如:(int)、(long long)等。投射操做符的優先級僅次於單⽬操做符,⽐乘法操做符優先級⾼。
2.2.3 浮點數與浮點數的轉換以及浮點數與整數之間的轉換
浮點數之間的轉換與整數之間的轉換不一樣。由於⼀般處理器架構採⽤的是IEEE754規格化浮點表⽰法,因此⼤多數⼗進制浮點數⽆法精確地⽤⼆進制浮點數來表⽰,這是因爲其尾數部分實際上都是經過2n進⾏相加擬合⽽成的。因此,咱們在⽐較兩個浮點數的時候必須謹慎使⽤==相等性操做符。 若是⼀個處理器架構⽀持IEEE754標準,那麼單精度浮點與雙精度浮點的轉換直接可根據IEEE754標準中浮點數的表⽰⽅式進⾏。對於單精度浮點數轉雙精度浮點數,雙精度浮點數的符號位以及尾數⾼位有效數都不須要變更,僅僅是尾數低位添0;⽽階碼部分則是⽤原始單精度浮點的指數加上雙精度浮點數指定的中經指數誤差便可。⽽雙精度轉單精度則可能會產⽣精度丟失。 C11標準提到,⼀個有限浮點型實數(即它不是⼀個⾮數NaN,也不是⼀個⽆窮⼤數INF)能夠轉換爲除布爾類型以外的其餘全部整數類型,而後其⼩數部分被丟棄,也就是說它的值向零截斷。若是⼀個浮點數轉爲較低精度的⽆符號整數(⽐如⼀個單精度浮點數轉爲⼀個⽆符號8位整數),那麼該浮點數是先轉爲與它相同寬度的帶符號整數(先轉signed int),而後再轉爲相應更低精度的⽆符號整數(再轉unsigned char),仍是直接轉爲與低精度⽆符號整數相同寬度的帶符號整數(先轉signed char),而後再轉爲該相同精度的⽆符號整數(再轉unsigned char),這⼀點在標準中沒有提到,也是由實現定義的。一樣,當⼀個整數轉爲⼀個浮點數時,假若⽬標浮點數⽆法容納原始整數的數值範圍,那麼結果也是未定義的。