0. 何谓“数据类型”
程序的主要功能就是处理数据,要处理数据就选输入数据、输出数据。
数据类型是程序的基础,它告诉我们数据的意义以及我们能在数据上执行的操作(运算)。
C++中支持广泛的数据类型,如下图(参考《C++ Primer 第5版》):
本文主要介绍基本内置类型相关的内容,关于自定义数据类型和复合类型相关内容,因为内容繁多,会整理成其他文章。
1. 基本内置类型
1.1 算数类型
1.1.1 尺寸与机器实现
1.1.1.1 基本内置类型的尺寸
C++标准没有规定基本内置类型的尺寸和大小,只规定了尺寸的最小值,允许编译器赋予这些类型更大的尺寸。
(参考《C++ Primer(第5版)》)
类型 | 含义 | 最小尺寸 |
---|---|---|
bool | 布尔类型 | 未定义 |
char | 字符 | 8位 |
wchar_t | 宽字符 | 16位 |
char16_t | Unicode字符 | 16位 |
char32_t | Unicode字符 | 32位 |
short | 短整型 | 16位 |
int | 整型 | 16位 |
long | 长整型 | 32位 |
long long | 长整型 | 64位 |
float | 单精度浮点数 | 6位有效数字 |
double | 双精度浮点数 | 10位有效数字 |
long double | 扩展精度浮点数 | 10位有效数字 |
C++规定
- 一个int至少和一个short一样大;
- 一个long至少和一个int一样大;
- 一个long long至少和一个long一样大。
那么,数据到底是如何存储在计算机的内存中的呢?
1.1.1.2 内置类型的机器实现
计算机以二进制存储数据:
01001011001010100010101001000110 ...
// 0x4B2A2A46
大多数计算机以8位二进制(1个字节,byte)为一个单元访问处理数据
左侧的数字表示每个字节的地址,计算机通过该地址一次访问到8bit的数据。
假设在某平台上,计算机以小端方式存储:
- 若一个int是32bit,则地址736424 - 736427的内容是
1261054534
。 - 若一个char是8bit,则地址736424显示的内容是字母
K
,地址736427的内容是F
。
1.1.2 signed与unsigned
除去布尔类型和扩展的字符型之外,其他的整型可以划分为有符号(signed) 和无符号(unsigned) 两种。
类型int、short、long和long long默认带符号,添加unsigned
关键字可以将其修饰为无符号。
unsigned int ui = 80;
类型char、signed char和unsigned char是三种不同的类型,char表现为signed char和unsigned中的任意一种,由具体编译器决定。
无符号类型不会小于0,因为在机器实现上,计算机将首位地址作为符号位,0正1负。无符号表明首位地址也参与到数据的表示中。
举上述的例子来说,若将字面值0x4B2A2A46
赋值给int类型,计算机解析出地址736424 - 736427的内容是1261054534
,因为736424的首位为0,其余位的内容是1261054534;若将736424的首位改为1,即0xCB2A2A46
,则地址736424~736427的内容是-886429114
(补码)。
若将字面值0x4B2A2A46
赋值给unsigned int类型,计算机解析出内容依旧是1261054534
;若将首位改为1,即0xCB2A2A46
,则计算机解析出的内容是3408538182
。
int a = 0x4B2A2A46;
cout << a << endl; // 1261054534
int b = 0xCB2A2A46;
cout << b << endl; // -886429114
unsigned int c = 0x4B2A2A46;
cout << c << endl; // 1261054534
unsigned int d = 0xCB2A2A46;
cout << d << endl; // 3408538182
1.1.3 字面值常量
字面值常量(literal) 就是我们写在代码文字中的80、'a’这样。
每个字面值常量也是对应一种数据类型的:
20 // 十进制,int
024 // 八进制,int
0x14 // 十六进制,int
20u // unsigned int
20l // long
20ul // unsigned long
20ll // long long
3.1415 // 小数表示,double
3.2e-3 // 科学计数法,double
3.14f // float
3.14l // long double
'a' // 字符,char
"hello world" // 字符串
'\n' // 转义字符
'\115' // 字符M
u"hello world" // Unicode16字符,char16_t
U"hello world" // Unicode32字符,char32_t
L'a' // 宽字符,wchar_t
u8"hello world" // utf-8字符串字面值,char
true // 布尔类型,bool
false // 布尔类型,bool
nullptr // 指针类型字面值,可以转换成任意类型的指针
注意:没有short,布尔类型只有两个。
1.1.4 类型转换
类型转换是C++中比较复杂的一类运算,它分为隐式类型转换和显示类型转换。
隐式类型转换无须程序员介入,在保证运算对象类型一致且不丢失精度的情况下,编译器自动进行。这类转换发生条件众多且情况多样,可以发生在整型内部(整型提升等),也可以发生在整型到浮点型,甚至是整型到自定义类型(转换构造函数)。
显示类型转换是一种程序员可以控制的转换,可以使用如下四种运算符:
- static_cast
- dynamic_cast
- const_cast
- reinterpret_cast
上述详细内容请参考【深度C++】之“类型转换”。
2. 复合类型
复合类型是指基于其他类型而定义的类型,例如int &ra = a
就是基于int类型定义的一个int引用。
2.1 数组
数组(array) 是存放类型相同的对象的容器,通过对象在数组中的位置访问对象。
详细内容,请参考【深度C++】之“数组”。
2.2 引用
引用为对象起了另外一个名字,实现了对其他对象的间接访问。
int ival = 20;
int &ref_val = ival; // ref_val引用ival
引用是C++引入的用来简化指针操作的一个类型,它分为左值引用和右值引用。
引用不是一个对象,因此不能定义引用的引用,引用在声明的时候必须初始化,初始化之后不能对其赋值(直接赋值相当于使用被引用对象)、拷贝。
详细内容,请参考【深度C++】之“引用”。
2.3 指针
指针是指向另外一种类型的复合类型。与引用类似,也实现了对其他对象的间接访问。
指针不同于引用,是一个对象,因此可以定义指向指针的指针,允许对指针进行拷贝、赋值,声明指针不必初始化,和其他内置类型一样,也存在各种初始化方式。
与指针相关的运算符有
- 取地址运算符
&
- 解引用运算符
*
int ival = 20;
int *p_val = &ival; // 通过取地址运算符&,定义p_val是指向ival的指针
cout << *p_val << endl; // 通过解引用运算符*,访问指针的值
详细内容,请参考【深度C++】之“指针”。
2.4 const限定符
const限定符可以作用域上述的任何一种数据类型,包括被unsigned修饰或者指针、引用等复合类型。
关于const限定符一共有8类用法,请参考【深度C++】之“const”
3. 自定义数据类型
3.1 结构体
结构体(struct) 是自定义数据类型的一种方式,与class用法类似,但也有区别。
C++中使用关键字struct
来定义一个结构体。
详细内容,请参考【深度C++】之“类与结构体”
3.2 类
类(class) 是面向对象的灵魂,它体现了面向对象抽象和封装的思想,是C++中自定义数据类型的一种方式。
C++中使用关键字class
来定义一个类。
详细内容,请参考【深度C++】之“类与结构体”
3.3 联合体
联合体(union) 是一种特殊的类。一个union可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。
详细内容,请参考【深度C++】之“联合体”
3.4 枚举
枚举(enumeration) 使我们可以将一组整型常量组织在一起。和类一样,每个枚举类型定义了一种新的类型。枚举属于字面值常量类型。
枚举包含限定作用域枚举和不限定作用于枚举。
详细内容,请参考【深度C++】之“枚举”
4. 定义变量
C++中定义变量的规则如下:
类型说明符 声明符列表;
- 类型说明符(type specifier):包括基本数据类型(base type)、auto类型说明符、decltype类型说明符;
- 声名符 列表:由多个声名符(declarator) 组成,每个声名符由
,
隔开。每个声名符可以包含类型修饰符。在声名符中定义标识符,并且可以进行初始化。 ;
分号结尾
举例:
int a,b = 0;
int
是类型说明符,是一个基本数据类型;a, b = 0
是声名符列表,此处包含2个声名符,没有类型修饰符。标识符a
、b
。b
使用=
初始化为0。;
分号结尾
int a = 2, *pa, &ra = a;
int
是类型说明符,是一个基本数据类型;a = 2, *pa, &ra = a
是声名符列表,此处包含3个声名符,有类型修饰符*
和&
。标识符a
、pa
、ra
。a
使用=
初始化为2,ra
使用=
将引用a
。;
分号结尾
5. 总结
C++中支持广泛的数据类型,包括基本内置类型、自定义数据类型和复合类型。
基本内置类型包括算数类型和空类型。算数类型包括整型和浮点型。
自定义数据类型一共四种:结构体、类、联合体、枚举。
复合类型包括数组、引用、指针和const限定符。
各种类型之间存在着一种普遍支持的运算:类型转换。