C++学习之三、C++基本数据类型

C++ 内置数据类型分两大类:基本数据类型复合数据类型。本编介绍基本数据类型,又称算术(arithmetic)类型。

基本数据类型——整型

整型是指没有小数部分的数字,如 2, 98, -376 和 0。术语宽度( width )用于描述存储整数时使用的内存量。使用的内存越多,则越宽。
C++ 的基本整型按宽度递增的顺序排列,分别有 charshortintlong 和 C++11 新增的 long long,每种类型都有有符号和无符号版本,因此总共有 10 种类型可供选择。另外,ANSI/ISO C++ 还新增了一种特殊类型 bool

数值整型

数值型整型包括 shortintlong 和 C++11 新增的 long long

整型宽度

概念:C++ 字节由至少能够容纳实现了基本字符集的相邻位组成,也就是说 C++ 一个字节可能意味着大于 8 bit,可能为 8,16 或 32 bit,通常一个字节为 8 bit。

C++ 提供了一种灵活的标准,它确保了最小长度(从 C 语言借鉴而来):

  • short 至少 16 位;
  • int 至少与 short 一样长;
  • long 至少 32 位,且至少与 int 一样长;
  • long long 至少 64 位,且至少与 long 一样长。

Tips: 上述 4 种整型都有一种不能存储负数值的无符号变体,其优点是可以增大变量能够存储的最大值,只需使用关键字 unsigned 来声明即可:

1
2
3
4
5
unsigned short n_short; // 无符号 short 类型
unsigned int n_int; // 无符号 int 类型
unsigned n_int; // 默认为 int 类型,int 可省略,仍然为无符号 int 类型
unsigned long n_long; // 无符号 long 类型
unsigned long long n_llong; // 无符号 long long 类型

可使用运算符 sizeof 确定数据类型的宽度。

1
2
cout << "int is " << sizeof (int) << " bytes.\n";
cout << "int is " << sizeof n_int << " bytes.\n";

limits 头文件通过预编译(#define)方式,提供了一些符号常量用以确定数据类型的取值范围:

符号常量 表示
CHAR_BIT char 的位数
CHAR_MAX char 的最大值
CHAR_MIN char 的最小值
SCHAR_MAX signed char 的最大值
SCHAR_MIN signed char 的最小值
UCHAR_MAX unsigned char 的最大值
SHRT_MAX short 的最大值
SHRT_MIN short 的最小值
USHRT_MAX unsigned short 的最大值
INT_MAX int 的最大值
INT_MIN int 的最小值
UINT_MAX unsigned int 的最大值
LONG_MAX long 的最大值
LONG_MIN long 的最小值
ULONG_MAX unsigned long 的最大值
LLONG_MAX long long 的最大值
LLONG_MIN long long 的最小值
ULLONG_MAX unsigned long long 的最大值

选择整型类型

C++ 提供了如此多的整型,使用时应该怎么去选择呢?

  • 通常,int 被设置为对目标计算机而言最为“自然”的长度,计算机处理起来效率最高。如果没有其它理由,则应使用 int
  • 如果变量表示的值不可能为负,则可以使用无符号类型,这样变量可以表示更大的值。
  • 如果变量表示的整数值可能大于 16 位整数的最大值,则使用 long。即使系统上 int 为 32 位也应该这样做。这样,将程序移植到 int 为 16 位的系统时,就不会突然无法正常工作。
  • 如果要存储的值超过 20 亿,可使用 long long
  • 由于 shortint 小,使用 short 可以节省内存。通常,仅当有大型整型数组时,才有必要使用 short。如果节省内存很重要,则应使用 short 而不是使用 int

整型字面值

  1. 整型字面值,常用为四种不同的计数方式:

    • 十进制:第一位为 1~9,如 53
    • 二进制:所有位都以 0 和 1 来表示,如 0101 等于十进制中 6
    • 八进制:第一位为 0,第二位为 1~7,如 042 等于十进制中的 34
    • 十六进制:以 0x 或 0X 开头,如 0x42 等于十进制的 66

    进制计数只是为了人为的计数方便,在计算机中,数据最终都是以二进制存储。

    Tips: cout 输出默认为十进制,cout 提供了控制符来控制显示 dechexoct。具体如下:

    1
    2
    3
    4
    5
    6
    7
    8
    int a = 42;
    cout << a; // 默认打印为十进制:42
    cout << hex; // 切换输出为十六进制
    cout << a; // 输出打印为十六进制 2a
    cout << oct; // 切换输出为八进制
    cout << a; // 输出打印为八进制 52
    cout << dec; // 切换输出为十进制
    cout << a; // 输出打印为十进制 42;
  2. C++ 如何确定常量的类型

    如程序是如何确定常量 1000 是存储为 intlong 还是其它类型呢?

    1. 默认存储为 int
    2. 数值后的后缀。后缀有 L(Long)、U(Unsigned int)、LL(long long) 及组合后缀。
    3. 数值的长度。在 C++ 中,对十进制整数采用的规则与十六进制及八进制的不同。对于不带后缀的十进制整数,使用 intlonglong long 中能够存储该数的最小类型来表示。对于不带后缀的十六进制或八进制,使用下面几种类型中能够存储该数的最小类型来表示:intunsigned intlongunsigned longlong longunsigned long long

另一种整型 —— char 类型

char 是比 short 更小的整型,专为存储字符(字母或数字)而设计。它足够长,能够表示目标计算机系统中所有的基本符号——所有的字母、数字、标点符号。一般占用 8 位。

char 类型特点

char 在默认情况下既不是没有符号也不是有符号。是否有符号由 C++ 实现决定,这样编译器开发人员可以最大限度地将这种类型与硬件属性匹配起来。char 可以显示地将类型设置为 signed charunsigned char:

1
2
3
char fodo;  // 可能有符号,也可能是无符号
unsigned char bar; // 显式定义为无符号类型
signed char snark; // 显式定义为有符号类型

char 类型变量初始化使用单引号:char c = 'M'
C++ 将字符表示为整数,可以进行算术运算。如 char c = 'M' + 1;
使用 cin 输入时都是读取的字符,然后强转为其它类型存储。

char 字面值

  • 以单引号括起来。如 'M''N''''5'
  • 不能直接通过键盘输入的字符使用转义序列。如'\a''\t''\n'
    C++ 转义序列的编码
字符名称 ASCII 符号 C++ 代码 十进制 ASCII 编码 十六进制 ASCII 编码
换行符 NL(LF) \n 10 0xA
水平制表符 HT \t 9 0x9
垂直制表符 VT \v 11 0xB
退格 BS \b 8 0x8
回车 CR \r 13 0xD
振铃 BEL \a 7 0x7
反斜杠 \ \\ 92 0x5C
问号 ? \? 63 0x3F
单引号 \‘ 39 0x27
双引号 \“ 34 0x22

Tips: 应该像处理常规字符那样处理转义字符(如 \n)。也就是说,将它们作为字符常量时,应用单引号括起来;将它们放在字符串中时,不要使用单引号。
可以基于字符的八进制和十六进制编码来使用转义序列。例如 Ctrl+Z 的 ASCII 码为 26,对应的八进制编码为 032,十六进制编码为 0x1a。可以用下面的转义序列来表示该字符:\032\0x1a。将这些编码用单引号括起,可以得到相应的字符常量。

char 引申 —— 通用字符名

C++ 实现支持基本的源字符集(可用来编写源代码的字符集),基本的执行字符集(程序执行期间可处理的字符,如换行、退格、振铃等),还有一种表示特殊字符的机制,提供扩展源字符和扩展执行字符集,它独立于任何特定的键盘,使用的是通用字符名(universal character name)。

通用字符名的用法类似于转义字符。以 \u 后面跟 4 个十六进制位 或 \U 后面跟 8 个十六进制位来表示。这些十六进制位表示的是字符的 ISO 10646 码点(一种正在制定的国际标准,为大量的字符提供数值编码)。

如果所用的实现支持扩展字符,则可以在标识符(如字符常量)和字符串中使用通用字符名。如

int k\u00F6rper;
cout << "Let them eat g\u00E2teau.\n";

ö 的 ISO 10646 码点为 00F6,而 â 的码点 00E2 。因此,上述代码将变量名设置为 körper,将显示下面的输出

Let them eat gâteau.

如果系统不支持 ISO 10646,它将显示其它字符或 gu00E2teau,而不是 â

char 引申 —— wchar_t 宽字符类型

wchar_t 宽字符类型是一种整数类型,它的长度和符号特征随实现而异。它有足够的空间,可以表示系统使用的最大扩展字符集。这种类型与另一种整型(底层[underlying]类型)的长度和符号属性相同。对底层类型的选择取决于实现,因此在一个系统中,它可能是 unsigned short,而在另一个系统中,则可能是 int

cincout 将输入和输出看作是 char 流,因此不适于用来处理 wchar_t 类型。 iostream 头文件的最新版本提供了作用相似的工具 - wcinwcout,可用于处理 wchar_t 流。另外,可以通过加上前缀 L 来指示宽字符常量和宽字符串。

wchar_t bob = L'P'; // 宽字符常量
wcout << L"tall";   // 输出宽字符串

C++11 新增类型:char16_t 和 char32_t

区别于 wchar_t 长度及符号由实现决定,char16_tchar32_t 长度分别为 16 位和 32 位,无符号类型。使用前缀 uU 分别来表征这两种字符或字符串常量,如 u'C' u"work hard"U'R' U"First try".
char16_tchar32_t 也有底层类型 —— 一种内置的整型,但底层类型可能随系统而异。

bool 类型

ANSI/ISO C++ 标准添加了一种名叫 bool 的新类型,布尔变量的值可以是 truefalse,这两个值都可以通过提升转换为 int 类型,true 被转换为 1,false 被转换为 0。

bool is_ready = true;

任何数字值或指针值都可以被隐式转换为 bool 值。任何非零值都被转换为 true,而零被转换为 false

基本数据类型 —— 浮点型

浮点数能够表示带小数部分的数字,如 3.14159, 23.56, 0.10 等。如果数字很大,无法表示为 long 类型,则可以使用浮点类型表示。浮点类型能够表示非常大或非常小的数,它们的内部表示方法与整数有天壤之别。浮点型由于存储方式不同,没有无符号类型。
有效位(significant figure)是指十进制数字中有意义的位(从第一个非零数字开始算起)。例如,山脉高度为 14179 英尺,该数字使用了 5 个有效位,如果写成约 14000 英尺,则该有效位只有 2 位,其余的 3 位只不过是占位符而已。

Tips: 0.00000000035 的有效位数为 2。

浮点数类型

C++ 按有效位数和允许的指数最小范围从低到高分为 3 种浮点类型:floatdoublelong double

浮点数有效位数

事实上,CC++ 对于有效位数的要求如下:

  • float 至少 32 位;
  • double 至少 48 位,且不少于 float
  • long double 至少和 double 一样多;
  • 以上三种类型的有效位数可以一样多。然而,通常,float 为 32 位,double 为 64 位,long double 为 80、96 或 128 位。
  • 以上三种类型的指数范围至少是 -37 到 37。

头文件 cfloatfloat.h 对系统的限制作了说明(cfloat 是 C 语言的 float.h 文件的 C++ 版本)。下面列出 Borland C++ Builderfloat.h 文件中一些批注项:

1
2
3
4
// 以下为对浮点型有效位的限制
#define FLT_DIG 6 // float 类型,至少 6 位有效位数(针对十进制,下同)
#define DBL_DIG 15 // double 类型,至少 15 位有效位数
#define LDBL_DIG 18 // long double 类型,至少 18 位有效位数

浮点数字面值

在程序中书写浮点常量时,程序将把它存储为哪种浮点类型呢?

  • 默认情况下,浮点常量将存储为 double 类型。
    8.3 0.14 0.1e7 3.99e-5 默认都是 double 类型
  • 使用后缀存储为指定类型。float 后缀为 fFlong double 后缀为 lL
    1
    2
    3
    4
    1.234f  // float 常量
    2.3E20F // float 常量
    2.2L // long double 常量
    2.2 // 默认为 double 常量

浮点数优缺点

与整数相比,浮点数有两大优点。首先,它们可以表示整数之间的值。其次,由于有缩放因子,它们可以表示更大的范围。缺点方面,浮点运算的速度通常比整数运算慢,且精度将降低。

一些总结归纳

数值整型和浮点型字面值,使用后缀来表征不同类型,如 10U, 100UL 。但对于 char 类型,使用前缀来表征不同的类型,如 u'a' U'a' L'b'