2.1 基本内置类型
闲话少叙,先把基本的内置类型列举出来:
类型 | 含义 | 最小存储空间 | 整型/浮点型 |
---|---|---|---|
bool | 布尔型 | —— | 整型 |
char | 字符型 | 8位 | 整型 |
wchar_t | 宽字符型 | 16位 | 整型 |
short | 短整型 | 16位 | 整型 |
int | 整型 | 16位 | 整型 |
long | 长整型 | 32位 | 整型 |
float | 单精度浮点型 | 6位有效数字 | 浮点型 |
double | 双精度浮点型 | 10位有效数字 | 浮点型 |
long double | 扩展精度浮点型 | 10位有效数字 | 浮点型 |
Tips:
虽然
C++
标准规定了每一种算数类型的最小存储空间,但并不能组织编译器
使用更大的存储空间。以int
为例,几乎所有的编译器使用的存储空间都比16位
要大。8位
的块为一个字节
,32位
或4个字节
为一个“字”
。只有在知道相应内存地址对应的字节或字所表示的算术类型后,才能确定其真正表示的含义。例如:内存地址为736425
的字节01110001
,如果我们知道其算术类型为8位无符号整数
,那么其代表了整数112
;若其代表了ISO-Latin-1
中的一个字符,那么其代表了小写字母q
。除
bool
以外,其他整型
类型均可分为signed
(带符号的)和unsigned
(不带符号的)。int
,short
,long
默认为signed
; 为使用unsigned
类型必须先声明,如unsigned long
;unsigned int
可简化表示为unsigned
。对于整型赋值这件事来说,当声明的数据类型为
unsigned
的时候,需要对超出范围的(如336
)和负数的值(如-1
)取模,在进行赋值。首先,针对8位
的unsigned char
, 那么336%256 = 1 * 256 + 80
,80
为最终的模数;而对于-1 % 256 = -1 * 256 + 255
,则255
为最终的模数。
Postscript:
可能你也会跟我一样,被上面的求模给折腾个够呛。经过一番搜索与探寻,我们从原理上来探究一下这个问题。
首先,我们先来看看C++
中在内存里是通过什么形式来存储数据的。这里我们抛出三个概念:原码、反码、补码。《第二章 变量和基本类型——2.1 基本内置类型((转)附录之原码、反码和补码及其背后的数学之美)》
原码 简单来说就是真真正正表示具体数值的码,最高位为符号位,其余各位表示二进制数值。如
1
表示为0000 0001
;-3
表示为1000 0011
。用原码是没法直接来解决上述正数加负数问题的,所以伟大的计算机科学家们就开动了脑筋,创造了反码和补码这一神器的东西。反码 为了简化计算机的运算方式,用加法替代减法(
1-1 = 1+(-1) = 0
),所以出现了反码。反码的概念是通过求模和同余思想发展而来的。通过求反如5
表示为0000 0101
(正数的反码为本身);-3
表示为1111 1100
(负数的反码为除去符号位求反),使用反码进行相加得到的反码为0000 0001
, 注意①,这里出现了进位,需要在求得反码和的基础上加上进位1
,从而求得最终的反码0000 0010
。注意②,针对得到的最终反码和,还需要恢复成原码才能正确表示其真值,对于上述结果0000 0010
,首位为0
,其为正数,则反码与原码相同;若首位为1
,则要将其余各位取反,方可得到最终正确的原码。补码 顾名思义,就是要补一下反码的缺陷吧,我是这么觉得的,不过更深层的数学解释是将模增加
1
!这背后伟大的数学原理还请大家详见本节补充内容之《第二章 变量和基本类型——2.1 基本内置类型((转)附录之原码、反码和补码及其背后的数学之美)》。使用补码进行求和,正数的补码为本身,负数的补码需先将原码转换成反码在加1
。5
的补码表示为0000 0101
;-3
的补码表示为1111 1101
,使用补码进行相加得到的补码和为0000 0010
, 注意,针对得到的最终补码和,还需要恢复成原码才能正确表示其真值,对于上述结果0000 0010
,首位为0
,其为正数,则反码与原码相同;若首位为1
,则要将其余各位取反,方可得到最终正确的原码。
通过以上的阐述我们明白了在计算机的内存中到底是以什么形式存储数据的,没错,就是补码 。那我们知道-1
的补码为1111 1111
,所以当我们将-1
赋值给unsigned
变量时,左手边第一位符号位变成了数字位,所以最终的结果将会变成255
。
综上所述,当把超出范围的数值(正数或者负数)赋给unsigned
变量时,编译器会调整越界值使其满足要求,编译器会自动将该值对unsigned
类型的取值数目进行求模,然后取所得值;当把超出范围的数值(正数或者负数)赋给signed
变量时,也不会报错,但是最后的取值却与编译器有关,不同的编译器可能会采取不同的处理方法,但是大多数的编译器依然是采取和unsigned
类型一样的处理方法,也就是对该类型的取值数目求模后的值。但是这中做法不能得到保证。具体的示例可以参考《第二章 变量和基本类型——2.1 基本内置类型((转)附录之C++中整型的超范围赋值问题)》