
C++入门——4.基本数据类型
基本数据类型简介
在变量中,我们初次使用了变量,其定义需要指定一个数据类型,以下将介绍基本数据类型。
基本数据类型
下面是基本数据类型的列表:
类型 | 类别 | 含义 | 样例 |
---|---|---|---|
float double long double | 浮点数 | 有分数部分的数字 | 3.1415926 |
bool | bool 整型 | true 或 false | true |
char / wchar_t / char8_t (C++20) / char16_t (C++11) / char32_t (C++11) | 字符 整型 | 一个单独的字符 | ‘c’ |
short int / int / long int / long long int (C++11) | 整数 整型 | 含0,正数或负数 | 42 |
std::nullptr_t (C++11) | Null Pointer | 空指针 | nullptr |
void | Void | 无类型 | n/a |
后缀(_t
)
在较新版本的C++中定义了许多类型(如,空指针 std::nullptr_t
)使用 _t
后缀。这个后缀表示 type(类型),他是应用于现代类型的一个常见术语。
但许多类型没有_t后缀,这是不同版本引入的不一致导致的。
Void
Void
表示“没有类型”,也叫做空类型,和空指针不是一个东西。
Void 是不完整类型,因此无法被定义
不返回值函数
那Void有什么用呢?
答:可以为函数指定不需要返回值,用于一些只执行操作的函数。
void writeValue(int x) {
std::cout << "The value of x is: " << x << "\n";
// 无return语句
}
但此时如果返回了任意值,都会导致程序报错:
[Error] return-statement with a value, in function returning 'void' [-fpermissive]
[错误] 返回带有值的返回语句,在函数中返回 'void'[-fpermissive]
对象大小和sizeof运算符
对象大小
由于我们通常通过变量名(而不是直接通过内存地址)访问内存,编译器向我们隐藏了给定对象使用多少字节。当我们访问某个变量x时,编译器知道要检索多少字节的数据(基于变量x的类型),并可以为我们处理该任务。
基本数据类型大小
类别 | 类型 | 最小大小(字节) | 通常大小(字节) |
---|---|---|---|
布尔 | bool | 1 | 1 |
字符 | char | 1 | 1 |
wchar_t | 1 | 2或4 | |
char8_t | 1 | 1 | |
char16_t | 2 | 2 | |
char32_t | 4 | 4 | |
整数 | short | 2 | 2 |
int | 2 | 4 | |
long | 4 | 4或8 | |
long long | 8 | 8 | |
浮点数 | float | 4 | 4 |
double | 8 | 8 | |
long double | 8 | 8或12或16 | |
指针 | std::nullptr_t | 4 | 4或8 |
sizeof运算符
为了确定特定机器上数据类型的大小,C++提供了一个名为sizeof
的运算符。
sizeof运算符是一元运算符,它接受类型或变量,并返回其大小(以字节为单位)。
示例程序:
#include <iostream>
#include <iomanip> // ÒýÈë std::setw (
int main()
{
std::cout << std::left;
std::cout << std::setw(16) << "bool:" << sizeof(bool) << "bytes\n";
std::cout << std::setw(16) << "char:" << sizeof(char) << " bytes\n";
std::cout << std::setw(16) << "short:" << sizeof(short) << " bytes\n";
std::cout << std::setw(16) << "int:" << sizeof(int) << " bytes\n";
std::cout << std::setw(16) << "long:" << sizeof(long) << " bytes\n";
std::cout << std::setw(16) << "long long:" << sizeof(long long) << " bytes\n";
std::cout << std::setw(16) << "float:" << sizeof(float) << " bytes\n";
std::cout << std::setw(16) << "double:" << sizeof(double) << " bytes\n";
std::cout << std::setw(16) << "long double:" << sizeof(long double) << " bytes\n";
return 0;
}
当然,sizeof也可以直接用于变量:
#include <iostream>
#include <iomanip> // ÒýÈë std::setw (
int main()
{
int x{};
std::cout << "x is " << sizeof(x) << " bytes\n";
return 0;
}
有符号整数
整数可以表示为 正整数 和 负整数,如:1, -1, 2, 0
等。
C++中提供了4种基本整数类型:
类型 | 最小大小(字节) | 注 |
---|---|---|
short int | 2 | |
int | 2 | 现代机器上通常是4个字节 |
long int | 4 | |
long long int | 8 |
有符号整数
在C++中,也可以添加符号,用于表示有符号整数:
#include <iostream>
int main()
{
int x{ -1 };
std::cout << x << "\n";
return 0;
}
有符号整数范围
下面是一个包含不同大小的有符号整数范围的表:
类型大小 | 取值范围 |
---|---|
8位 | -128 to 127 |
16位 | -32,768 to 32,767 |
32位 | -2,147,483,648 to 2,147,483,647 |
64位 | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
溢出(Overflow)
当算术运算(如加法或乘法)试图在可以表示的范围之外创建值,则这称为整数溢出(或算术溢出)。对于有符号整数,整数溢出将导致未定义的行为。
通常,溢出会导致信息丢失,这几乎是不可取的。如果怀疑对象可能需要存储超出其范围的值,请使用范围更大的类型!
整数除法
对两个整数进行除法时,当结果为整数时,当结果为整数时,C++的运行结果于预期一致;
#include <iostream>
int main()
{
std::cout << 20 / 4 << "\n";
return 0;
}
对两个整数进行除法(称为整数除法)时,C++始终生成整数结果。由于整数不能保存分数,因此任何分数部分都会被丢弃(而不是取整!)。
#include <iostream>
int main()
{
std::cout << 8 / 5 << "\n";
return 0;
}
观察上方,当 $8/5$ 产生值 1.6。其删除了小数部分(0.6),保留1的结果。我们也可以说 $8/5$ 等于1余数3。
无符号整数
除了有符号整数, C++还支持无符号整数。 无符号整数只能保存非负整数
定义无符号整数
要定义无符号整数,我们使用 unsigned
关键字。它放在类型之前:
unsigned short us;
unsigned int ui;
unsigned long ul;
unsigned long long ull;
无符号整数范围
1字节无符号整数的范围为0到255。与1字节有符号整数的范围对比,两者都可以存储256个不同的值,但有符号整数将其范围的一半用于负数,所以无符号整数可以存储两倍大的正数。
下面的表格显示了无符号整数的范围:
类型大小 | 取值范围 |
---|---|
8位 | 0 to 255 |
16位 | 0 to 65,535 |
32位 | 0 to 4,294,967,295 |
64位 | 0 to 18,446,744,073,709,551,615 |
无符号整数溢出
如果我们试图将数字280(需要9位来表示)存储在1字节(8位)无符号整数中,会发生什么情况?答案是溢出。
让我们看一下使用2字节无符号整数的情况:
#include <iostream>
int main()
{
unsigned short x{ 65535 }; // 最大的16位无符号整数
std::cout << "x was: " << x << '\n';
x = 65536; // 65536 溢出, 所以重新取值
std::cout << "x is now: " << x << '\n';
x = 65537; // 65537 溢出, 所以重新取值
std::cout << "x is now: " << x << '\n';
return 0;
}
输出:
x was: 65535
x is now: 0
x is now: 1
因为 unsigned short 最大值为65535,那么65536溢出就会变为起始值0,65537就是1,以此类推。
避免使用无符号整数?
通过以下程序:
#include <iostream>
// int 是 4 字节
int main()
{
unsigned int x{ 2 };
unsigned int y{ 3 };
std::cout << x - y << '\n'; // 2 - 3 = 4294967295
return 0;
}
输出结果:
4294967295
如果使用的有符号整数,那么返回的就是-1了。使用无符号明显没达到想要的效果。
固定宽度整数和size_t
为什么整数变量的大小不固定?
C选择故意将整数的大小保持为可修改状态,以便编译器实现者可以为int选择在目标计算机体系结构上性能最好的大小。
考虑int类型。int的最小大小是2个字节,但在现代架构上通常是4个字节。如果假设int只有2个字节以确保最大兼容性,那么在int为4个字节的系统上,每个整数浪费2个字节,内存使用量加倍!
固定宽度整数
为了解决上诉问题,C99定义了一组固定宽度的整数(在stdint.h头中),这些整数在任何体系结构都保证大小相同。
类型 | 类别 | 范围 |
---|---|---|
std::int8_t | 一字节有符号 | -128 to 127 |
std::uint8_t | 一字节无符号 | 0 to 255 |
std::int16_t | 两字节有符号 | -32,768 to 32,767 |
std::uint16_t | 两字节无符号 | 0 to 65,535 |
std::int32_t | 四字节有符号 | -2,147,483,648 to 2,147,483,647 |
std::uint32_t | 四字节无符号 | 0 to 4,294,967,295 |
std::int64_t | 八字节有符号 | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
std::uint64_t | 八字节无符号 | 0 to 18,446,744,073,709,551,615 |
使用固定整数有以下缺点:
- 并非所有架构都支持固定宽度整数,在某些特殊架构(如奇特大型机或嵌入式平台)上可能导致程序无法编译,不过现代架构大多已标准化8/16/32/64位变量,该问题出现概率低。
- 在某些架构上,固定宽度整数可能比更大类型运行慢,尽管CPU处理特定类型速度快不代表程序整体快,因现代程序常受内存速度限制,大内存占用可能致程序运行变慢,具体需实际测量。
快速整数和至小整数
为了解决上述缺点,C++定义了两个可选整数集。
- 快速类型(
std::int_fast#_t
和std::uint_fast#_t
)- 提供最快的有符号/无符号整数类型,宽度至少为#位(其中#=8、16、32或64)
- 所谓最快,指的是CPU可以最快地处理的整数类型
- 至小类型(
std::int_least#_t
和std::uint_least#_t
)- 提供宽度至少位#位的最小有符号/无符号整数类型(其中#=8、16、32或64)。例如,
std::uint_least32_t
将提供最小的无符号整数类型,该类型至少位32位
- 提供宽度至少位#位的最小有符号/无符号整数类型(其中#=8、16、32或64)。例如,
示例:
#include <cstdint>
#include <iostream>
int main()
{
std::cout << "least 8: " << sizeof(std::int_least8_t) * 8 << "bits\n";
std::cout << "least 16: " << sizeof(std::int_least16_t) * 8 << " bits\n";
std::cout << "least 32: " << sizeof(std::int_least32_t) * 8 << " bits\n";
std::cout << '\n';
std::cout << "fast 8: " << sizeof(std::int_fast8_t) * 8 << " bits\n";
std::cout << "fast 16: " << sizeof(std::int_fast16_t) * 8 << " bits\n";
std::cout << "fast 32: " << sizeof(std::int_fast32_t) * 8 << " bits\n";
return 0;
}
输出:
least 8: 8 bits
least 16: 16 bits
least 32: 32 bits
fast 8: 8 bits
fast 16: 32 bits
fast 32: 32 bits
科学计数法
在初中数学中,我们学过一种简洁的方式书写冗长数字的速记方法——科学计数法。
在科学计数法中,将数字采用以下形式:有效位 x 10的指数。例如:12000写作$1.2*10^4$。
在C++中没办法直接输入指数,因此,我们使用字母“e”来表示方程的n次幂,如:$1.210^4$ 写为 1.2e4
,负数次幂就直接写负数 $5/100或0.05$ 可以写为 $510^-2$
如何将数字转换为科学计数法
步骤如下:
- 指数从0开始
- 滑动小数点,使小数点左侧只有一个非零数字。
- 向左滑动一位,指数加一
- 向右滑动一位,指数减一
- 删去任何前导零(在有效位的左端)
- 尾部的0如果有效,则不能删除
示例如下:
开始:0.0078900
小数点右滑3位:0007.8900e-3
删除前导0:7.8900e-3
保留尾部的0:7.89e-3 (5个有效位)
开始:600.410
小数点左滑2位:6.00410e2
无前导0:6.00410e2
保留尾部的0:6.00410e2(6个有效位)
下面是需要理解的最重要的一点:有效位中的数字 (“e”之前的部分)称为有效数字。有效位数定义数字的精度。有效位中的数字越多,数字就越精确。
浮点数
浮点数,也就是数学中的“小数”,其有三种不同的浮点数据类型:单精(float)、双精度(double)和长双精(long double)
其大小如下:
类型 | 最小大小 | 常见大小 |
---|---|---|
float | 4字节 | 4字节 |
double | 8字节 | 8字节 |
long double | 8字节 | 8,12或16字节 |
打印浮点数字
观察程序:
#include <iostream>
int main()
{
std::cout << 5.0 << "\n";
std::cout << 6.7f << "\n";
std::cout << 9876543.21 << "\n";
return 0;
}
输出结果:
5
6.7
9.87654e+006
其情况如下:
- 默认情况下,如果小数部分为0,则
std::cout
不会打印数字的小数部分。 - 正常打印
- 使用科学计数法
浮点范围
位数 | 范围 | 精度 |
---|---|---|
4 字节 | ±1.18 x 10^-38 to ±3.4 x 10^38 and 0.0 | 6-9 个有效位, 通常是 7 个 |
8 字节 | ±2.23 x 10^-308 to ±1.80 x 10^308 and 0.0 | 15-18 个有效位,通常 16 个 |
80 位 (通常占用12 或 16 字节) | ±3.36 x 10^-4932 to ±1.18 x 10^4932 and 0.0 | 18-21 个有效位 |
16 字节 | ±3.36 x 10^-4932 to ±1.18 x 10^4932 and 0.0 | 33-36 个有效位 |
浮点精度
当想要使用浮点数(小数)来表示分数$1/3$时,这个数字将会显示为 $0.33333333333333......$。
在C++中,浮点数的小数位不是无限的,其定义了浮点精度,用于限制表示的数字长度。
浮点数的精度定义了它可以表示多少个有效数字,而不会丢失信息。
浮点数默认的进度为6,如下程序:
#include <iostream>
int main()
{
std::cout << 9.87654321f << '\n';
std::cout << 987.654321f << '\n';
std::cout << 987654.321f << '\n';
std::cout << 9876543.21f << '\n';
std::cout << 0.0000987654321f << '\n';
return 0;
}
程序输出:
9.87654
987.654
987654
9.87654e+006
9.87654e-005
当然,默认的精度大小也可以更改,使用 std::setprecision()
的输出操纵函数覆盖 std::cout
显示的默认精度。
#include <iomanip> // 引入 std::setprecision()
#include <iostream>
int main()
{
std::cout << std::setprecision(17); // 输出时,进度保留17位
std::cout << 3.33333333333333333333f << "\n"; // f 意味着 float 类型
std::cout << 3.33333333333333333333 << "\n"; // 没有后缀意味着 double 类型
return 0;
}
输出:
3.3333332538604736
3.3333333333333335
虽然精度设置为了17位,但单精度的还是有些误差,但双精度的误差就比较小了。
布尔值
布尔变量
布尔变量只有两个可能的变量:【true或false】
声明布尔变量,需要使用关键字 bool:
bool b;
初始化布尔值示例:
bool b1 { true };
bool b2 { false };
b1 = false;
bool b3 {}; // 默认初始化成false
正如一元减号运算符(-)可以用于使整数为负一样,逻辑NOT运算符(!)可以用于将布尔值从true翻转为false,或从false翻转为true:
bool b1 { !true };
bool b2 { !false };
if语句简介
if语句只有在满足条件的情况下才会执行语句内的代码,最简单的if语句采用以下形式:
if (条件表达式) 语句;
条件表达式
:计算的结果为布尔结果的表达式,如:
aa==bb
:aa等于bb,如果等于就会执行语句,不等于就不执行aa!=bb
:aa不等于bb,如果不等于就执行语句,等于就不执行
位了可读性,通常也可以写成如下:
if (条件表达式)
语句;
使用if语句的示例程序
#include <iostream>
int main()
{
std::cout << "Enter an integer: ";
int x {};
std::cin >> x;
if (x == 0)
std::cout << "The value is zero\n";
return 0;
}
结果如下:
if-else
上面的例子中,我们成功处理了输入数字为0的情况,如果我们想高数用户他们输入的数字不为0时,该怎么办?
可以使用 if-else
,其语法如下:
if (条件语句)
语句
else
语句
修改后的程序:
#include <iostream>
int main()
{
std::cout << "Enter an integer: ";
int x {};
std::cin >> x;
if (x == 0)
std::cout << "The value is zero\n";
else
std::cout << "The value is non-zero\n";
return 0;
}
串联if语句(if-else if-else)
有时想要按顺序判断几个条件的真假,我们可以使用 if
或 if - else if -else
来实现,如下:
#include <iostream>
int main()
{
std::cout << "Enter an integer: ";
int x {};
std::cin >> x;
if (x > 0)
std::cout << "The value is positive\n";
else if (x < 0)
std::cout << "The value is negative\n";
else
std::cout << "The value is zero\n";
}
演示:
布尔返回值域if语句
函数的返回值也可以做为if语句的判断条件:
#include <iostream>
// x与y相等返回true, 否则返回false
bool isEqual(int x, int y)
{
return x == y;
}
int main() {
std::cout << "Enter an integer: ";
int x {};
std::cin >> x;
std::cout << "Enter another integer: ";
int y {};
std::cin >> y;
if (isEqual(x, y))
std::cout << x << " and " << y << " are equal\n";
else
std::cout << x << " and " << y << " are not equal\n";
}
Enter an integer: 1
Enter another integer: 2
1 and 2 are not equal
非布尔条件
也就是相反条件,比如 1 != 2
就是 1不等于2
也可以直接用于反转最终的布尔返回值,如:
#include <iostream>
// x与y相等返回true, 否则返回false
bool isEqual(int x, int y)
{
return x == y;
}
int main() {
std::cout << "Enter an integer: ";
int x {};
std::cin >> x;
std::cout << "Enter another integer: ";
int y {};
std::cin >> y;
if (!isEqual(x, y))
std::cout << x << " and " << y << " are equal\n";
else
std::cout << x << " and " << y << " are not equal\n";
}
最后的结果将会相反:
Enter an integer: 1
Enter another integer: 2
1 and 2 are equal
字符类型
作用:字符型变量显示单个字符
语法:char c = 'x';
tip:
- 在显示字符类型时,只能使用
'
(单引号),不能使用"
(双引号)- 单引号内只能有一个字符,多个需要使用字符串
示例代码:
#include <iostream>
int main()
{
// 字符串的定义
char ch = 'a';
std::cout << ch << "\n";
std::cout << sizeof(char) << "\n";
// 错误定义演示
// ch = "abcde" // 字符不能使用双引号,且不能有多个字符同时存在
// ch = 'abcde'; // 不能有多个字符同时存在
std::cout << (int)ch << "\n"; // 查看字符a对应的ASCII码
ch = 97; // 可以直接使用ASCII给字符型变量赋值
std::cout << ch << "\n";
system("pause");
return 0;
}
ASCII码表格:
ASCII值 | 控制字符 | ASCII值 | 字符 | ASCII值 | 字符 | ASCII值 | 字符 |
---|---|---|---|---|---|---|---|
0 | NUT | 32 | (space) | 64 | @ | 96 | 、 |
1 | SOH | 33 | ! | 65 | A | 97 | a |
2 | STX | 34 | " | 66 | B | 98 | b |
3 | ETX | 35 | # | 67 | C | 99 | c |
4 | EOT | 36 | $ | 68 | D | 100 | d |
5 | ENQ | 37 | % | 69 | E | 101 | e |
6 | ACK | 38 | & | 70 | F | 102 | f |
7 | BEL | 39 | , | 71 | G | 103 | g |
8 | BS | 40 | ( | 72 | H | 104 | h |
9 | HT | 41 | ) | 73 | I | 105 | i |
10 | LF | 42 | * | 74 | J | 106 | j |
11 | VT | 43 | + | 75 | K | 107 | k |
12 | FF | 44 | , | 76 | L | 108 | l |
13 | CR | 45 | - | 77 | M | 109 | m |
14 | SO | 46 | . | 78 | N | 110 | n |
15 | SI | 47 | / | 79 | O | 111 | o |
16 | DLE | 48 | 0 | 80 | P | 112 | p |
17 | DCI | 49 | 1 | 81 | Q | 113 | q |
18 | DC2 | 50 | 2 | 82 | R | 114 | r |
19 | DC3 | 51 | 3 | 83 | S | 115 | s |
20 | DC4 | 52 | 4 | 84 | T | 116 | t |
21 | NAK | 53 | 5 | 85 | U | 117 | u |
22 | SYN | 54 | 6 | 86 | V | 118 | v |
23 | TB | 55 | 7 | 87 | W | 119 | w |
24 | CAN | 56 | 8 | 88 | X | 120 | x |
25 | EM | 57 | 9 | 89 | Y | 121 | y |
26 | SUB | 58 | : | 90 | Z | 122 | z |
27 | ESC | 59 | ; | 91 | [ | 123 | { |
28 | FS | 60 | < | 92 | / | 124 | | |
29 | GS | 61 | = | 93 | ] | 125 | } |
30 | RS | 62 | > | 94 | ^ | 126 | ` |
31 | US | 63 | ? | 95 | _ | 127 | DEL |
字符串类型
作用:用于表示一串字符
声明方式:
- C风格:
char 变量名[] = "字符串值"
#include <iostream>
using namespace std;
int main()
{
char str1[] = "hello world";
cout << str1 << endl;
system("pause");
return 0;
}
- C++风格:
string 变量名 = "字符串值"
#include <iostream>
using namespace std;
int main()
{
string str1 = "hello world";
cout << str1 << endl;
system("pause");
return 0;
}
转义字符
作用:表示一些特殊作用的字符串
常用的有:
\n
:表示换行
cout << "hello" << "\n" << "world";
\t
:制表符(TAB键),长用于固定多个字符的间隙
cout << "hello" << "\t" << "world\t" << "!";
转义字符还能表示的特殊就是系统保留的ASCII字符,正常无法输出,可以使用\x
来表示,如:
\\
:表示\
\'
:表示'
转义字符 | 含义 | ASCII码值(十进制) |
---|---|---|
\a | 警报 | 007 |
\b | 退格(BS) ,将当前位置移到前一列 | 008 |
\f | 换页(FF),将当前位置移到下页开头 | 012 |
\n | 换行(LF) ,将当前位置移到下一行开头 | 010 |
\r | 回车(CR) ,将当前位置移到本行开头 | 013 |
\t | 水平制表(HT) (跳到下一个TAB位置) | 009 |
\v | 垂直制表(VT) | 011 |
\ | 代表一个反斜线字符"" | 092 |
' | 代表一个单引号(撇号)字符 | 039 |
" | 代表一个双引号字符 | 034 |
? | 代表一个问号 | 063 |
\0 | 数字0 | 000 |
\ddd | 8进制转义字符,d范围0~7 | 3位8进制 |
\xhh | 16进制转义字符,h范围0 | 3位16进制 |
- 感谢你赐予我前进的力量