C++
的类型
按照标准,C++
只有两种类型:基本类型和复合类型,但是里面细节多导致彻底理解它们有难度,所以这里只是简单总结一下。
基本类型
基本类型分成算术类型和两种特殊的类型。
算术类型
算术类型分成整数类型和浮点数类型两种。
整数类型
以下都是整数类型:
有符号整数类型 | 无符号整数类型 | |
---|---|---|
标准整数类型 | 标准有符号整数类型 | 标准无符号整数类型 |
扩展整数类型 | 扩展有符号整数类型 | 扩展无符号整数类型 |
下面两个是属于上面的标准整数类型部分:
- 标准有符号整数类型:
signed char
,short int
,int
,long int
,long long int
- 标准无符号整数类型:数量与标准有符号整数类型一一对应,除了
signed char
外直接在前面加关键字unsigned
就得到对应的无符号整数类型,比如unsigned short int
,不过这点标准文档没直说。
这份最小宽度表格抄自C++
标准文档,顺便算了一下它们按照最小宽度能表示的范围的极限,其中long long
表示范围比较大采用科学计数法表示。
类型 | 最小宽度N | 有符号最小值 | 有符号最大值 | 无符号最大值 |
---|---|---|---|---|
signed char | 8 | -128 | 127 | 256 |
short | 16 | -32768 | 32767 | 65536 |
int | 16 | -32768 | 32767 | 65536 |
long | 32 | -2147483648 | 2147483647 | 4294967296 |
long long | 64 | -9.22x10^18 | 9.22x10^18 | 1.84x10^19 |
一个具有宽度N的有符号整数类型的取值范围是,大于或等于-2^(N-1)
,小于或等于2^(N-1)-1
;无符号整数类型对应是大于等于0
,小于等于2^N
。
除了上面几个之外,下面这些也是整数类型,但是在标准里被单独罗列:
- 普通字符类型:
char
,signed char
,unsigned char
。其中char
类型是特殊的类型,由特定的编译器决定里面实际是signed char
或是unsigned char
。 - 窄字节字符类型:由普通字符类型和
char8_t
组成。char8_t
隐藏类型是unsigned char
。 char16_t
和char32_t
,这两个也属于整数类型。(它们隐藏类型是uint_least16_t
和uint_least32_t
,但是这两个隐藏类型不是整数类型)。wchar_t
类型。它是特殊的类型,宽字节字符,由特定编译器决定是有符号或者无符号整数,它能表示所有的国际长字节字符。- 类型
bool
,它是由编译器决定的无符号整数类型,取值范围是true
或false
。它没有sign
,unsigned
,short
,long
这四种前缀。
整数类型示例
#include <iostream>
using namespace std;
int main() {
int i;
cout << "输出0到9:" << endl;
for (i = 0; i < 10; i++) {
cout << i << " ";
}
cout << endl;
return 0;
}
输出:
输出0到9:
0 1 2 3 4 5 6 7 8 9
演示bool
:
#include <iostream>
using namespace std;
int main() {
char noticeTrue[] = "TRUE";
char noticeFalse[] = "FALSE";
bool condition = false;
if (condition) {
cout << noticeTrue << endl;
} else {
cout << noticeFalse << endl;
}
return 0;
}
输出:
$ ./a.out
FALSE
另外,标准规定了整数类型的最小值,最大值是没有限制的,所以实际上获取最小最大值结果可能是:
#include <iostream>
using namespace std;
int main() {
cout << "type int ~ min=" << numeric_limits<int>::min() << endl;
cout << "type int ~ max=" << numeric_limits<int>::max() << endl;
return 0;
}
运行程序:
$ ./a.out
type int ~ min=-2147483648
type int ~ max=2147483647
跟除了C语言之外的其它高级编程语言相比,C++的整数类型比较复杂。在实际用的时候整数一般是用int
类型定义,除非长度不够再换更大范围的。名字里面带有char
字样的一般是定义成数组,用来存储字符串,UTF8编码的用char8_t
,UTF16用char16_t
,诸如此类。char
类型比较特殊,除了ASCII
编码的字符串外,涉及网络传输、文件读写等,需要一块区域做缓存的时候一般用char
定义一块连续内存空间。bool
用法就是存储真或者假,相当于别的编程语言的布尔类型。
浮点数类型
三种:float
,double
,long double
,这三种中,后面的类型范围大于前面的类型范围。至于具体的范围是多大由具体的编译器决定。可以用std::numeric_limits
类获取具体编译器上的最大、最小值(适用于整数、浮点数类型)。
浮点数类型使用示例
#include <iostream>
using namespace std;
int main() {
float a, b, c;
a = 1.01;
b = 3.99;
c = a * b;
cout << "c=" << c << endl;
return 0;
}
运行结果
c=4.0299
获取最大最小值示例(不同的机器上可能结果不一样)
#include <iostream>
using namespace std;
int main() {
cout << "min=" << numeric_limits<long double>::min() << endl;
cout << "max=" << numeric_limits<long double>::max() << endl;
return 0;
}
运行结果
$ ./a.out
min=3.3621e-4932
max=1.18973e+4932
其它类型
除了算术类型之外的两种类型,在C++
标准中这两个是单独作为两个类型来说明的。
cv void
,使用CV限定符(const
和volatile
)修饰的void
——也就是const void
和volatile void
的统称,它跟void
是不一样的std::nullptr_t
。命名空间std
里面的nullptr_t
类型。
复合类型
笼统地说,C++
复合类型包括:
- 数组
- 函数
- 引用
- 类和结构体
- 联合体
- 枚举类型
- 指针
数组
数组作用是存放相同类型的一组对象。数组声明方式:
类型 名称 [可选的常量表达式] 可选的其它限定符序列
例如下面的fibonacci
就是一个数组:
#include <iostream>
using namespace std;
int main() {
int i;
int fibonacci[6] = {1, 1};
cout << "计算前" << endl;
for (i = 0; i < 6; i++) {
cout << "i=" << i << ", value=" << fibonacci[i] << endl;
}
for (i = 2; i < 6; i++) {
fibonacci[i] = fibonacci[i - 2] + fibonacci[i - 1];
}
cout << "计算后" << endl;
for (i = 0; i < 6; i++) {
cout << "i=" << i << ", value=" << fibonacci[i] << endl;
}
return 0;
}
运行程序将输出:
计算前
i=0, value=1
i=1, value=1
i=2, value=0
i=3, value=0
i=4, value=0
i=5, value=0
计算后
i=0, value=1
i=1, value=1
i=2, value=2
i=3, value=3
i=4, value=5
i=5, value=8
浮点数数组
#include <iostream>
using namespace std;
int main() {
float x[] = {1.1, 2.2, 3.3}; // 初始化可以用 {} ,非初始化不能这样用
for (int i = 0; i < 3; i++) {
cout << "i=" << i << ", value=" << x[i] << endl;
}
return 0;
}
输出:
$ ./a.out
i=0, value=1.1
i=1, value=2.2
i=2, value=3.3
也可以声明多维数组,例如2维:
#include <iostream>
using namespace std;
int main() {
int matrix[3][4];
int i, j, n = 0;
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) {
matrix[i][j] = ++n;
}
}
cout << "二维数组的内容:" << endl;
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) {
cout << "matrix(" << i << "," << j << ")=" << matrix[i][j] << " ";
}
cout << endl;
}
return 0;
}
输出:
二维数组的内容:
matrix(0,0)=1 matrix(0,1)=2 matrix(0,2)=3 matrix(0,3)=4
matrix(1,0)=5 matrix(1,1)=6 matrix(1,2)=7 matrix(1,3)=8
matrix(2,0)=9 matrix(2,1)=10 matrix(2,2)=11 matrix(2,3)=12
函数
C++
中函数是一种类型,函数的名称可以当成变量名称用,能进行一些特定的操作。比如:
#include <iostream>
using namespace std;
int x (int a, int b);
int main() {
int (*z) (int a, int b);
z = &x;
cout << (void *)&x << endl;
cout << (void *)z << endl;
cout << (*z)(3, 9) << endl;
return 0;
}
int x (int a, int b) {
return a + b;
}
* 运行结果,z保存的地址就是函数x的地址,于是(*z)就是函数x。
0x4008ec
0x4008ec
12
引用
如下,类型名称后面用符号&表示这是对应的引用类型:
#include <iostream>
using namespace std;
int main() {
int x = 100;
int& y = x; // y 引用了 x
cout << "x=" << x << endl;
cout << "y=" << y << endl; // 读取 y 就是读取 x
y = 12; // 对 y 赋值就是对 x 赋值
cout << "x=" << x << endl;
cout << "y=" << y << endl;
return 0;
}
输出:
x=100
y=100
x=12
y=12
类和结构体
标准中的描述是:包含一系列各种类型的对象、枚举和用于操纵这些对象的函数,以及对访问这些实体的一些限制。类是一种用途比较复杂的数据类型,这里简单做示例,更多内容单独整理一份文档。下面的class
可以无缝替换为struct
,它们都可用作定义类的关键字,其功能有点区别但是差不了多少。
#include <iostream>
using namespace std;
class Student {
public:
Student(float age) {
this->age = age;
}
void sayYourAge() {
cout << "我今年" << age << "岁了。" << endl;
}
private:
float age;
};
int main() {
Student xiaoMing = Student(14);
Student xiaoWang = Student(17);
xiaoMing.sayYourAge();
xiaoWang.sayYourAge();
return 0;
}
运行结果:
$ ./a.out
我今年14岁了。
我今年17岁了。
联合体
这里只简单介绍一下C++
中的联合体union
。union
能够在相同内存空间存储不同类型的对象。具体允许存入怎样的对象可以在定义时声明。在读取union
中保存的对象时,按照什么方式读取决于访问成员。这是一个非常特殊的地方,也是联合的特征。另外,同struct一样,联合默认访问权限也是公有的,并且也具有成员函数。
#include <iostream>
using namespace std;
union Exams {
float average;
int score;
char level;
};
int main() {
Exams englishExams;
englishExams.average = 76.342;
englishExams.score = 80;
englishExams.level = 'A'; // 由于union类型里面数据成员是保存进相同的地址,所以后面覆盖前面的,只有'A'是最终存进去的东西
cout << "average=" << englishExams.average << " 地址:" << (void *)&(englishExams.average) << endl;
cout << "score=" << englishExams.score << " 地址:" << (void *)&(englishExams.score) << endl;
cout << "level=" << englishExams.level << " 地址:" << (void *)&(englishExams.level) << endl;
return 0;
}
* 输出:
average=9.10844e-44 地址:0x7ffd15cee06c
score=65 地址:0x7ffd15cee06c
level=A 地址:0x7ffd15cee06c
枚举类型
枚举类型比较简单,它相当于给按顺序给自定义的常量赋予了自增值,也可以在定义时给需要枚举的常量指定一个值(值可以重复)。不能假设里面对应的值是int
或者unsigned int
类型,编译器会看情况选择更大范围的类型去存储。
用法:
#include <iostream>
enum Level {
A = 100,
B = 80,
C = 60
};
int main() {
Level exam = A;
if (exam == A || exam == B) {
std::cout << "还可以" << std::endl;
} else {
std::cout << "差" << std::endl;
}
return 0;
}
运行:
$ ./a.out
还可以
指针
- 指向任何对象的指针和指向
const volatile void
、const void
或volatile void
的指针都属于C++的复合类型。这种对象的类型称为“对象指针类型”,下面例子的a
,b
,x
,y
,z
都是对象指针类型。
int main() {
const void *x; // x 是复合类型
volatile void *y; // y 是复合类型
const volatile void *z; // z 是复合类型
int* a; // 指向 int 类型的指针是复合类型
float* b; // 指向 float 类型的指针是复合类型
return 0;
}
- 具有类型的指向函数(包括类的静态成员)的指针。示例:
#include <iostream>
class Example {
public:
static void sayHello() {
std::cout << "你好啊,我是Example类的静态成员函数" << std::endl;
}
};
void sayHello(); // 在main之前声明函数,当然也可以直接把函数定义在这里,效果相同
int main() {
/* 声明pointerToSayHello是一个无参数且返回void类型的函数指针,
* 并初始化其值为sayHello的地址。该函数指针指向的是普通的函数。
*/
void (*pointerToSayHello)() = &sayHello;
/* 声明pointerToExampleSayHello是一个无参数且返回void类型的函数指针,
* 并初始化其值为Example::sayHello的地址。该函数指针指向的是静态成员函数。
*/
void (*pointerToExampleSayHello)() = &(Example::sayHello);
// 可以像调用函数一样执行:
(*pointerToSayHello)();
(*pointerToExampleSayHello)();
return 0;
}
void sayHello() {
std::cout << "你好啊" << std::endl;
}
* 运行的结果是:
$ ./a.out
你好啊
你好啊,我是Example类的静态成员函数
注:指向对象结尾(如数组结尾)、空指针值或非法指针值的指针也算指针类型。指向cv void
或者非cv void
的指针可以用来指向不知道是什么类型的对象,这种指针能够容纳任何类型的对象指针,cv void *
类型的对象具有跟cv char *
类型的对象相同的表示和对齐特性。