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),这⼀点在标准中没有提到,也是由实现定义的。一样,当⼀个整数转为⼀个浮点数时,假若⽬标浮点数⽆法容纳原始整数的数值范围,那么结果也是未定义的。