还记得之前的篮子与瓶子吗?它们作为不同类型物质的容器,被针对性地以不同的材料与形状设计出来,以适应人们的日常需求。
想一想,瓶子可以用塑料、陶瓷或是蜡纸这类隔水的材料制作,而篮子得用麻绳、布料或是橡胶这类易于变形的材料制作。它们都是装载物质的容器,但它们所承载的物质类型不同,也提醒了人们如何选择正确的容器。
在 C++ 中亦是如此——我们所持有的数据决定了承载该数据的容器类型;同样地,容器的类型亦反过来决定我们应向其装载的数据类型。
下面,我们就来探寻 C++ 中最基本的一批容器吧。
0
这是什么?对,还是它,数字零!
零是什么?首先它肯定是个数。再细分以下,它还是个整数,是一个既不可算作正数、亦不可算作负数的整数。
我们可以使用类型为int
的容器存储数字零,int
是英语单词 integer (整数) 的缩写。在这里,我们先随便取个容器名a
:
int a = 0;
类型必须写在开头,其后是容器的名字,而等于号之后便是我们想要装载的内容物。
在 C++ 中,我们称这样的容器叫作变量。变量本质上就是具名的数据,它有具体的可承载数据的类型,并且也有一片用于放置此类型数据的空间。就拿刚刚的例子来讲,int
类型的每个变量都最少占据 4 字节的空间。
如同刚刚的程序所作的操作,是声明一个变量。在声明的同时,我们还可以通过在气候紧跟一个等于号与欲赋予的值,使得这个变量被初始化。在这个例子中,我们便将数字0
作为变量a
的初始值。在变量受到初始化的同时,它也就相当于被定义为了某一个值。
难道 C++ 只提供了一个int
类型么?当然不是。光是表达整数这个事情,C++ 就给了我们一大批的整数类型供使用,不过就是其所能表达的数字范围有所区别罢了:
类型 | 最小表达范围 | 通常表达范围 |
---|---|---|
char |
||
short 或 short int
|
||
int |
||
long 或 long int
|
||
long long 或 long long int
|
一般来讲,我们使用int
或long
类型便足以满足日常需求了。更广的数字表达范围也许能方便我们表达更大的数字,但由此也将会带来更大的空间占用;同样的,更窄的数字表达范围虽然能够很好地减少空间占用,但它也仅适用于对空间占用要求严格、需表达的整数又较小的场合。
回忆先前有关二进制数在计算机中如何表达的知识,我们知道整数的最开头以为用于表达数字的正负,气候才是数字本身的内容。
有没有办法让最开头那一位也作为数字呢?有!在类型前加unsinged
即可:
unsigned int a = 0;
如果我们为某个整数类型加上unsigned
前缀,它将不能表达负数,并且正数的表达范围也会变为原来的 2 倍。它通常适用于保证用不到负数并且也不使用负数的场合,例如计算物体间的距离——再怎么调整,我们都找不到距离为负数的两个物体。
这样不带正负号的类型称作无符号类型,由其承载的正数也可称作无符号数。无符号是整数类型的特权,除此之外的其它任何类型都不能也无法添加unsigned
前缀。与之相反,带符号的类型被称作有符号类型,可通过在类型前加signed
来声明。
默认情况下,除了char
类型以外的整数类型均为有符号类型,无需额外在类型前加signed
前缀。char
类型默认情况下是否有符号,是根据具体所在的运行环境而定的。
无符号类型的数字表达范围其实与其对应的有符号类型的一样宽,形象地讲其实也就是把表示负数的那部分范围“挪”到了右边:
类型 | 最小表达范围 | 通常表达范围 |
---|---|---|
unsigned char |
||
unsigned short 或 unsigned short int
|
|
|
unsigned int |
||
unsigned long 或 unsigned long int
|
||
unsigned long long 或 unsigned long long int
|
除了整数之外,我们有时也需要表达更为复杂的小数,而这种数字无法完整储存在整数类型的变量中。
C++ 提供了另一批类型,即浮点数类型,可使我们得以储存一定精度的小数:
类型 | 表达范围 |
---|---|
float |
最低 6 位有效数字,小数位数 |
double |
最低 15 位有效数字,小数位数 |
我们一般会选择double
作为首选的浮点数类型,不仅仅是因为其足够高的精度,也因为现代大多数的设备都对double
类型有硬件上的效率优化——甚至要比较小float
类型还快。
精度一词,实际上指的是该类型可表达的最大有效位数,而非数字小到多少位,或是数字大到多少位。例如,以下数字虽然有着悬殊的大小差距,但其精度其实是相等的:
$1.23168809 × 10^{91}$
$3.14159263 × 10^0$
$2.19857822 × 10^{-82}$
还记得最初我们向屏幕打印的话么?
"Hi, C++ World!"
这看起来更像是一系列字符,而不是什么数字。
那么,字符使用什么类型的变量储存呢?答案是char
类型:
char a = 'a';
char b = 'b';
char abc[] = "abc";
变量a
与b
分别仅存储了一个字符,而变量abc
却因为后面跟了个[]
,结果它却可以接受以双引号包裹的 C 风格字符串。
不必惊讶,我们将在后续的章节学习有关数组的内容,而刚刚的变量abc
便是将要介绍的数组的一种特殊形式——字符数组,也就是刚刚所提到的 C 风格字符串。
除了char
类型,C++ 也提供了一批其它的字符类型,便于我们使用其它的字符集表达文字:
类型 | 占用空间大小 | 说明 |
---|---|---|
char |
1 字节 | 普通字符类型 |
wchar_t |
2 字节 | 宽字符类型 |
char8_t (C++20) |
1 字节 | Unicode 字符类型,8 位版 |
char16_t |
2 字节 | Unicode 字符类型,16 位版 |
char32_t |
4 字节 | Unicode 字符类型,32 位版 |
这些类型与先前介绍的一批字符字面量是相互匹配的,亦因此这些字符字面量便也是有对应类型的变量容纳的。
总结以下,我们认识了以下的变量类型:
- 用于表达整数的
char
、short
、int
、long
与long long
- 用于修饰整数类型、使之成为无符号数的
unsigned
- 用于表达浮点数的
float
、double
- 用于表达字符的
char
、wchar_t
、char8_t
、char16_t
与char32_t
类型是 C++ 的重要组成部分,它构成了 C++ 的大部分内容。接下来,我们将学习更深层次的类型系统。
- 录入下面的程序,尝试修改程序中的变量名与初始值并允许,体验一下定义变量的感觉。
#include <iostream>
int main()
{
int apple = 1;
int pen = 1;
char pineapple = 'a';
std::cout << "I have " << apple << " apple" << std::endl
<< "I have " << pen << " pen" << std::endl
<< "Uh! Apple-pen!" << std::endl;
<< " I have " << pen << " pen" << std::endl
<< " I have " << pineapple << " pineapple" << std::endl
<< "Uh! Pineapple-pen!" << std::endl;
return 0;
}