4.2 数据类型
4.2.1 概括
C++的基本类型分为两组: 一组由存储为整数的值组成,另一组由存储为浮点格式的值组成。
整型之间通过存储值时使用的内存量及有无符号来区分。整型从最小到最大依次是: bool、 char、 signed char、unsigned char、short、unsigned short、int、unsigned int、long、unsigned long以及C++ 11新增的long long和unsigned long long。还有一种wchar_ t类型,它在这个序列中的位置取决于实现。C++11新增了类型char16_ t和char32_t, 它们的宽度足以分别存储16和32位的字符编码。C++确保了char足够大,能够存储系统基本字符集中的任何成员,而wchar_ t则可以存储系统扩展字符集中的任意成员,short至少为16位,而int至少与short一样长,long至少为32位,且至少和int一 样长。确切的长度取决于实现。
字符通过其数值编码来表示。I/0系统决定了编码是被解释为字符还是数字。
浮点类型可以表示小数值以及比整型能够表示的值大得多的值。3种浮点类型分别是float、double和long double。C+ +确保float不比double长,而double不比long double长。通常,float使用32位内存,double使用64位,long double使用80 ~ 128位。
4.2.3 整型类型
4.2.3.1 整型short、int、long和long long
整数:正整数、负整数和0统称为整数。即:没有小数部分的数字。
计算机程序语言只能表示所有整数的一个子集,即整数不能无限大和无限小,因为计算机内存资源有限。
整型之间通过存储值时使用的内存量及有无符号来区分。
机器字长:是指计算机进行一次整数运算所能处理的二进制数据的位数(整数运算即定点整数运算)。机器字长也就是运算器进行定点数运算的字长,通常也是CPU内部数据通路的宽度。现在一般为32位即4个字节,也有64位和16位的。
计算机内存的基本单元是位(bit),计算机机内存的基本度量单位字节(Byte)
整型数据的存储方式为按二进制数形式存储,例如十进制整数89的二进制形式为1011001,其在内存中的存储形式如图表示:
在整型符号int和字符型符号char的前面,可以加修饰符signed(表示“有符号”)或unsigned(表示“无符号”),如果指定为signed,则数值以补码形式存放,存储单元中的最高位(bit)用来表示数值的符号。如果指定为unsigned,则数值没有符号,全部二进制都用来表示数值本身。
中文名称 | 单位名称 | 英文全称 | 换算 |
位 | bit(最小单位) | bit | |
字节 | B | byte | 1B = 8 bit |
千 字节 | KB | KiloByte | 1KB = 1024 B |
兆 字节 | MB | MegaByte | 1MB = 1024 KB |
吉 字节 | GB | GigaByte | 1GB = 1024 MB |
太 字节 | TB | TeraByte | 1TB = 1024 GB |
拍 字节 | PB | PetaByte | 1PB = 1024 TB |
艾 字节 | EB | ExaByte | 1EB = 1024 PB |
皆 字节 | ZB | ZetaByte | 1ZB = 1024 EB |
佑 字节 | YB | YottaByte | 1YB = 1024 ZB |
珀 字节 | BB | Brontobyt | 1BB = 1024 YB |
诺 字节 | NB | NonaByte | 1NB = 1024 BB |
刀 字节 | DB | DoggaByte | 1DB = 1024 NB |
说明:
(1)类型修饰符signed和unsigned用于修饰字符型和整形。
(2)类型修饰符short和long用于修饰字符型和整形。
(3)当用signed和unsigned、short和long修饰int整形时,int可省略。
(4)其中bool和wchar_t是C++特有的。
(5)short至少16位;
(6)int至少 与short一样长;
(7)long至少32位,且至少与int一样长;
(8)long long最少64位,至少与long一样长。
#include <iostream>
#include<climits> //包含了整形限制的信息
#define ZERO 0
int main()
{
using namespace std;
int n_int = INT_MAX;
short n_short = SHRT_MAX;
long n_long = LONG_MAX;
long long n_llong = LLONG_MAX;
cout << "*********************************************" << endl;
cout << "C++提供了一种灵活的标准,它确保了最小长度" << endl;
cout << "short 至少16位" << endl;
cout << "int 至少与short一样长" << endl;
cout << "long 至少32位,且到少与int一样长" << endl;
cout << "long long 至少64位,且至少与long一样长" << endl << endl;
cout << "*****************有符号类型*****************" << endl;
cout << "char is " << sizeof(char) << " bytes." << endl;
cout << "int is " << sizeof(int) << " bytes." << endl;
cout << "short is " << sizeof(short) << " bytes." << endl;
cout << "long is " << sizeof(long) << " bytes." << endl;
cout << "long long is " << sizeof(long long) << " bytes." << endl;
cout << "\n";
cout << "Maximum values :" << endl;
cout << "chart:" << CHAR_MAX << endl;
cout << "int:" << n_int << endl;
cout << "short:" << n_short << endl;
cout << "long:" << n_long << endl;
cout << "long long:" << n_llong << endl << endl;
cout << "*****************无符号类型*****************" << endl;
short sam = SHRT_MAX;
unsigned short sue = sam;
cout << "Sam has " << sam << " dollars and Sue has " << sue << " dollars deposited." << endl;
cout << "Add $1 to each account." << endl;
sam = sam + 1;
sue = sue + 1;
cout << "Now Sam has " << sam << " dollars and Sue has " << sue << " dollars deposited.\nPoor Sam!" << endl;
sam = ZERO;
sue = ZERO;
cout << "Sam has " << sam << " dollars and Sue has " << sue << " dollars deposited." << endl;
cout << "Take $1 from each account." << endl ;
sam = sam - 1;
sue = sue - 1;
cout << "Now Sam has " << sam << " dollars and Sue has " << sue;
cout << " dollars deposited." << endl << "Lucky Sue!" << endl;
return 0;
}
该程序将一个short变量(sam) 和一个unsigned short变量(sue) 分别设置为最大的short值,在我们的系统上是32767。然后,将这些变量的值都加1。这对于sue来说没有什么问题,因为新值仍比无符号整数的最大值小得多;但sam的值从32767变成了-32768!同样,对于sam,将其设置为0并减去1,也不会有问题;但对于无符号变量sue,将其设置为0并减去后,它变成了65535。可以看出,这些整型变量的行为就像里程表。如果超越了限制,其值将为范围另一端的取值。C++确保了无符号类型的这种行为;但C++并不保证符号整型超越限制(上溢和下溢)时不出错,而这正是当前实现中最为常见的行为。
补充知识:
关于原码、反码和补码的介绍及转化关系
计算机中的符号数有三种表示方法,即原码、反码和补码。三种表示方法均有符号位和数值位两部分,是计算机中对数字的二进制定点表示方法。符号位都是用0表示“正”,用1表示“负”,而数值位,三种表示方法各不相同。下面依次介绍并给出相互的转换关系:
原码:
简单直观;例如,我们用8位二进制表示一个数,+11的原码为00001011,-11的原码就是10001011。但参加运算可能出错。例如数学上,1+(-1)=0,而在二进制中,00000001+10000001=10000010,换算成十进制为-2,显然出错了。
反码:
反码通常是用来由原码求补码或者由补码求原码的过渡码。反码跟原码是正数时,一样;负数时,反码就是原码符号位除外,其他位按位取反。
补码:
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
1、一个负整数(或原码)与其补数(或补码)相加,和为模。
2、对一个整数的补码再求补码,等于该整数自身。
3、补码的正零与负零表示方法相同。
总之,正整数的补码是其二进制表示,与原码相同。求负整数的补码,将其原码除符号位外的所有位取反(0变1,1变0,符号位为1不变)后加1。
即正整数的原码反码补码一样;负整数的反码是除了原码符号位其他位全部取反,补码是反码加一。
转换:
由于正数的原码、补码、反码表示方法均相同,不需转换,所以仅以负数情况分析。
(1) 已知原码,求补码。
例:已知某数X的原码为10110100,试求X的补码和反码。
解:由[X]原=10110100知,X为负数。求其反码时,符号位不变,数值部分按位求反;求其补码时,再在其反码的末位加1。
1 0 1 1 0 1 0 0 原码
1 1 0 0 1 0 1 1 反码(符号位不变,数值位取反)
1 1 0 0 1 1 00 补码 (补码再加一)
故:X的补码=11001100,X的反码=11001101。
(2) 已知补码,求原码。
分析:按照求负数补码的逆过程,数值部分应是最低位减1,然后取反。但是对二进制数来说,先减1后取反和先取反后加1得到的结果是一样的,故仍可采用取反加1 有方法。
例:已知某数X的补码11101110,试求其原码。
解:由X的补码=11101110知,X为负数。
采用逆推法
1 1 1 0 1 1 1 0 补码
1 1 1 0 1 1 0 1 反码(末位减1)
1 0 0 1 0 0 1 0 原码(符号位不变,数值位取反)
即为所求。
4.2.3.2 char类型
char类型是专门为存储字符(如字母或数字)而设计的。现在存储数字对于计算机来说不算什么,但存储字母则是另一回事。编程语言通过使用字母的数值编码存储字母;char类型就是为专存储字符(如字母和数字)而设计;char的取值范围为-128~127,它能够表示目标计算机系统中的所有基本符号 – 所有的字母、数字、标点符号等。
在美国,基本字符集通常是ASCII和EBCDIC字符集,它们都可以用8位来容纳,所以在使用这两种字符集的系统中,C++字节通常包含8位。然而,国际编程可能需要使用更大的字符集,如Unicode, 因此有些实现可能使用16位甚至32位的字节。C++推出了wchar_t类型。
在美国,最常用的符号集是ASCII字符集。字符集中的字符用数值编码表示。对于其它的编码,C++为了满足其需求提供了宽字符类型wchar_t类型可以存储更多的值。
字符字面值:’M’,将字符用单引号括起来。
using namespace std;
char ch = 'M';
int i = ch;
cout << "The ASCII code for " << ch << " is " << i << endl;
cout << "Add one to the character code: " << endl;
ch = ch + 1;
i = ch;
cout << "The ASCII code for " << ch << " is " << i << endl;
cout << "Displaying char ch using cout.put(ch): ";
cout.put(ch);
cout.put('!');
cout << endl << "Done" << endl;
输出内容:
The ASCII code for M is 77
Add one to the character code:
The ASCII code for N is 78
Displaying char ch using cout.put(ch): N!
Done
在程序中,'M'表示字符M的数值编码,因此将char变量ch初始化为'M',将把ch设置为77。然后,程序将同样的值赋给int变量i,这样ch和i的值都是77。接下来,cout把ch显示为M,而把i显示为77。如前所述,值的类型将引导cout选择如何显示值----这是智能对象的另一个例子。
由于ch实际上是一个整数,因此可以对它使用整数操作,如加1,这将把ch的值变为78。然后,程序将i重新设置为新的值(也可以将加1)。cout再次将这个值的char版本显示为字符,将int版本显示为数字。
C++将字符表示为整数这一事实,使得操纵字符值很容易。不必使用笨重的转换函数在字符和ASCII码之间来回转换。
cout.put()的使用说明:
cout.put()成员函数提供了另一种显示字符的方法,可以替代<<运算符。现在读者可能会问,为何需要cout.put()。答案与历史有关。在C++的Release 2.0之前,cout将字符变量显示为符,而将字符常量(如'M'和'N')显示为数字。问题是,C++的早期版本与C一样,也把字符常量存储为int类型。也就是说,'M'的编码77将被存储在一个16位或32位的单元中。而char变量一般占8位。下面的语句从常量'M'中复制8位(左边的8位)到变量ch中:
char ch = 'M';
遗憾的是,这意味着对cout来说,'M'和ch看上去有天壤之别,虽然它们存储的值相同。因此,下面的语句将打印$字符的ASCII码,而不是字符$:
cout << '$';
但下面的语句将打印字符$:
cout.put('$');
在Release 2.0之后,C++将字符常量存储为char类型,而不是int类型。 这意味着cout现在可以正确处理字符常量了。
4.2.3.3 bool类型
布尔数据类型代表真或假的值。也可以用非0值来表示真,用0来表示假。布尔类型在C++中是占用一个字节的。
从C99标准开始,类型名字为“_Bool”,如果你的编译器支持C99,就可以直接使用布尔类型。另外,C99为了让C和C++兼容,增加了一个头文件stdbool.h。里面定义了bool、true、false,让我们可以像C++一样的定义布尔类型。
也就是说在C99标准之前,C语言中是没有bool类型的。
4.2.4 浮点类型
C+ +也有3种浮点类型: float、 double和long double。这些类型是按它们可以表示的有效数位和允许的指数最小范围来描述的。
有效位(significant figure)是数字中有意义的位。例如,一斤白菜为1.35元,该数字使用了3个有效位,指出了白菜的具体金额。然而,将一斤白菜的价格写成约1.40元时,有效位数为2位,因为结果经过四舍五入精确到了角。在这种情况下,其余的1位只不过是占位符而已。有效位数不依赖于小数点的位置。例如,可以将金额写成13.5角。这样仍有3个有效位,因为这个值精确到了分。
事实上,C和C++对于有效位数的要求是,float至少32位,double至少48位,且不少于float, long double至少和double一样多。这三种类型的有效位数可以一样多。然而,通常,float为32位,double为64位,long double为80、96或128位。
#include <iostream>
int main()
{
using namespace std;
cout.setf(ios_base::fixed, ios_base::floatfield); // fixed-point
float tub = 10.0 / 3.0; // good to about 6 places
double mint = 10.0 / 3.0; // good to about 15 places
const float million = 1.0e6;
cout << "tub" << tub;
cout << ",a million tubs = " << million * tub;
cout << ", \nand ten million tubs = ";
cout << 10 * million * tub << endl;
cout << "mint =" << mint << "and a million mints = ";
cout << million * mint << endl;
return 0;
}
输出:
tub3.333333,a million tubs = 3333333.250000,
and ten million tubs = 33333332.000000
mint =3.333333 and a million mints = 3333333.333333
通常cout会删除结尾的零。例如,将3333333 .250000显示为3333333.25。调用cout.setf()将覆盖这种行为。这里要注意的是,为何float的精度比double低,tub和mint都被初始化为10.0/3.0 -- 3.3333333333333...... 由于cout打印6位小数,因此tub和mint都是精确的。 但当程序将每个数乘以一百万后,tub在第7个3之后就与正确的值有了误差。tub在7位有效位上还是精确的(该系统确保float至少有6位有效位,但这是最糟糕的情况)。然而,double类型的变量显示了13个3,因此它至少有13位是精确的。由于系统确保15位有效位,因此这就没有什么好奇怪的了。另外,将tub乘以一百万,再乘以10后,得到的结果不正确,这再一次指出了float的精度限制。
float精度是2^23,能保证6位。
double精度是2^52,能保证15位。
但是默认float和double都只能显示6位,再多需要#include ,然后在输出语句之前插入cout << setprecision(20);强制输出小数位。
扩展资料:
- 有些编译器 float的有效数字位是 8位 , 有些有效数字位是 7位
- 有些编译器double的有效数字位是 15位, 有些是 16位
- 注意printf(“%f”, x); // 默认输出6位小数(不要和有效数字混淆)
分析
- C/C++编译器标准都遵照IEEE制定的浮点数表示法来进行float,double运算
- 是float还是double,在内存中的存储主要分成三部分,分别是:
(1)符号位(Sign): 0代表正数,1代表负数
(2)指数位(Exponent): 用于存储科学计数法中的指数部分,并且采用移位存储方式
(3)尾数位(Mantissa): 用于存储尾数部分
- float 是 32位, 其中有23位用于存放尾数, 带有一个固定隐含位. 所以float有24个二进制有效位数.
- 2^24共有8个十进制位. 所以有些编译器 float的有效数字位是 8位 , 有些有效数字位是 7位.(注意不是小数的位数, 是有效数字位)
- double也一样,是64位, 其中有52位用于存放尾数, 一个固定隐含位. 共有 53个二进制有效位数.
2^53次方有15个十进制位, 所以有些编译器double的有效数字位是15位, 有些是16位