基本数据类型简介


变量中,我们初次使用了变量,其定义需要指定一个数据类型,以下将介绍基本数据类型。

基本数据类型

下面是基本数据类型的列表:

类型类别含义样例
float double long double浮点数有分数部分的数字3.1415926
boolbool 整型true 或 falsetrue
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
voidVoid无类型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的类型),并可以为我们处理该任务。

基本数据类型大小

类别类型最小大小(字节)通常大小(字节)
布尔bool11
字符char11
wchar_t12或4
char8_t11
char16_t22
char32_t44
整数short22
int24
long44或8
long long88
浮点数float44
double88
long double88或12或16
指针std::nullptr_t44或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 int2
int2现代机器上通常是4个字节
long int4
long long int8

有符号整数

在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

使用固定整数有以下缺点:

  1. 并非所有架构都支持固定宽度整数,在某些特殊架构(如奇特大型机或嵌入式平台)上可能导致程序无法编译,不过现代架构大多已标准化8/16/32/64位变量,该问题出现概率低。
  2. 在某些架构上,固定宽度整数可能比更大类型运行慢,尽管CPU处理特定类型速度快不代表程序整体快,因现代程序常受内存速度限制,大内存占用可能致程序运行变慢,具体需实际测量。

快速整数和至小整数

为了解决上述缺点,C++定义了两个可选整数集。

  • 快速类型(std::int_fast#_tstd::uint_fast#_t
    • 提供最快的有符号/无符号整数类型,宽度至少为#位(其中#=8、16、32或64)
    • 所谓最快,指的是CPU可以最快地处理的整数类型
  • 至小类型(std::int_least#_tstd::uint_least#_t
    • 提供宽度至少位#位的最小有符号/无符号整数类型(其中#=8、16、32或64)。例如,std::uint_least32_t将提供最小的无符号整数类型,该类型至少位32位

示例:

#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)

其大小如下:

类型最小大小常见大小
float4字节4字节
double8字节8字节
long double8字节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

其情况如下:

  1. 默认情况下,如果小数部分为0,则 std::cout 不会打印数字的小数部分。
  2. 正常打印
  3. 使用科学计数法

浮点范围

位数范围精度
4 字节±1.18 x 10^-38 to ±3.4 x 10^38 and 0.06-9 个有效位, 通常是 7 个
8 字节±2.23 x 10^-308 to ±1.80 x 10^308 and 0.015-18 个有效位,通常 16 个
80 位 (通常占用12 或 16 字节)±3.36 x 10^-4932 to ±1.18 x 10^4932 and 0.018-21 个有效位
16 字节±3.36 x 10^-4932 to ±1.18 x 10^4932 and 0.033-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位,但单精度的还是有些误差,但双精度的误差就比较小了。

布尔值


布尔变量

布尔变量只有两个可能的变量:【truefalse

声明布尔变量,需要使用关键字 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)

有时想要按顺序判断几个条件的真假,我们可以使用 ifif - 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:

  1. 在显示字符类型时,只能使用 '(单引号),不能使用 "(双引号)
  2. 单引号内只能有一个字符,多个需要使用字符串

示例代码:

#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字符
0NUT32(space)64@96
1SOH33!65A97a
2STX34"66B98b
3ETX35#67C99c
4EOT36$68D100d
5ENQ37%69E101e
6ACK38&70F102f
7BEL39,71G103g
8BS40(72H104h
9HT41)73I105i
10LF42*74J106j
11VT43+75K107k
12FF44,76L108l
13CR45-77M109m
14SO46.78N110n
15SI47/79O111o
16DLE48080P112p
17DCI49181Q113q
18DC250282R114r
19DC351383S115s
20DC452484T116t
21NAK53585U117u
22SYN54686V118v
23TB55787W119w
24CAN56888X120x
25EM57989Y121y
26SUB58:90Z122z
27ESC59;91[123{
28FS60<92/124|
29GS61=93]125}
30RS62>94^126`
31US63?95_127DEL

字符串类型


作用:用于表示一串字符

声明方式:

  1. C风格:char 变量名[] = "字符串值"
#include <iostream>

using namespace std;

int main()
{
	char str1[] = "hello world";
	cout << str1 << endl;
	
	system("pause");
	
	return 0;
}
  1. 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数字0000
\ddd8进制转义字符,d范围0~73位8进制
\xhh16进制转义字符,h范围09,af,A~F3位16进制