基本内置类型
算术类型
C++是一种静态数据类型语言,它的类型检查发生在编译时。因此,编译器必须知道每个变量对应的数据类型
那么,C++中哪些数据类型呢?
C++定义了一套包括算术类型和空类型(void)在内的基本数据类型。
基本算术类型可以分为整型、浮点型、字符型、布尔值。
详见下表:
注意: 使用 sizeof
关键字——可以统计数据类型/对象所占的内存大小。
需要关注的点:
- 有符号和无符号
有符号数(signed)是可以用来区分数值的正负。而无符号数(unsigned)仅有正值,没有负值。
当一个数是有符号数时,此时的最高位称为符号位。符号位为 1 时表示负数,为 0 时则表示正数。
有符号数和无符号数两者表示的范围不同,即同样长度的字节,有符号数比无符号数的最大值出现缩水。
-
单、双精度浮点数类型
float
可以表示大约 7 位有效数字;double
可以表示大约 15 位有效数字。如果计算需要高精度,或者需要处理极大或极小的数值范围,那么
double
可能更合适。然而,如果内存占用是一个重要因素,或者精度要求不是很高,那么
float
可能是一个更经济的选择。 -
字符型和字符串型
在 c 和 c++ 中,字符型变量占 1 字节。
char str[] = "hello world";
cout << sizeof(str) << endl; // 输出 12,因为编译器会在字符串结尾处添加一个空字符('\0')
#include<string>
string str = "hello world";
cout << sizeof(str) << endl; // 输出 40(32位下是28个字节、64位下是40个字节)
上面例子中,sizeof(str)
输出 40,这与string的内存分布相关。
- 宽字符
在 ASCII 字符编码中,每个字符由一个单字节(8位)表示。然而,这种编码无法表示许多非英语语言的字符,比如各种非拉丁文字(如中文、日文、阿拉伯文)或带有重音的字母。
宽字符wchar_t
通过使用多个字节来表示单个字符,来处理这种情况。
#include <iostream>
#include <locale> // 包含头文件以设置本地化
int main() {
// 设置本地化以支持宽字符输出
std::locale::global(std::locale(""));
wchar_t wideChar[] = L"こんにちは"; // 一个日语问候
std::wcout << wideChar << std::endl;
return 0;
}
注意:wchar_t
的确切大小取决于编译器和系统,但通常大于一个字节,常见的大小为 2 或 4 字节。
为了解决这个问题,C++11引入了 <uchar.h>
头文件,并定义了 char16_t
和 char32_t
类型,分别用于表示 16位 和 32位 的 Unicode 字符。这些类型的大小是固定的,分别为 2 字节和 4 字节,从而提供了更可靠的方法来处理 Unicode 字符,而不会受到不同实现之间的差异影响。
重识“类型转换”
类型转换我们已经足够熟悉了,所以在这里讲些可能不被人们熟悉的知识,以及强调几个问题。
static_cast 显式转换
static_cast
用于基本类型之间的转换,可以执行多种类型转换,但在安全性上比C风格转换更好。
double doubleValue = 3.14;
int intValue1 = (int)doubleValue; // C风格强制转换
int intValue2 = static_cast<int>(doubleValue); // 使用static_cast进行转换
问题 1:精度损失
当我们把一个整数值赋给浮点类型时,小数部分记为 0。
但是如果该整数所占的空间超过了浮点类型的容量,精度可能有损失。
#include <iostream>
int main() {
int intValue = 1234567890; // 一个大整数
double doubleValue = intValue;
std::cout << "Integer value: " << intValue << std::endl;
std::cout << "Double value: " << doubleValue << std::endl;
return 0;
}
Integer value: 1234567890
Double value: 1.23457e+09 //精度损失
即使我们使用了双精度浮点数(double
),它具有更多的位数,可以提供更高的精度。但只要超过一定范围的整数也可能在浮点数中存在精度损失的问题。
问题 2:数值越界
**情况一:**赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。
例如,8 比特大小的 unsigned char
可以表示 0 至 255 区间内的值,如果我们给它赋值 -1,则实际的结果是该值对 256 取模后所得的余数。因此,把 -1 赋给 8 比特大小的 unsigned char
所得的结果是255。
**情况二:**赋给带符号类型一个超出它表示范围的值时,结果是未定义的。此时,程序可能继续工作、可能崩溃,也可能生成垃圾数据。
问题 3:带无符号类型的表达式
尽管我们不会故意给无符号对象赋一个负值,却可能(特别容易)写出这么做的代码。
unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl; // 输出-84
std::cout << i + u << std::endl; // 输出4294967264
unsigned u1 = 20, u2 = 10;
std::cout << u1 - u2 << std::endl; // 输出10
std::cout << u2 - u1 << std::endl; // 输出4294967286
我们可以看到,当表达式里面既有带符号类型又有无符号类型时,带符号类型会自动转换为无符号类型。
因此,切勿混用带符号类型和无符号类型。
字面值常量
一个形如 42 的值被称作字面值常量(iteral),这样的值一望而知。每个字面值常量都对应一种数据类型,字面值常量的形式和值决定了它的数据类型。
整型和浮点型字面值
我们可以将将整型字面值写作十进制、八进制或十六进制。
20 /* 十进制 */ 024 /* 八进制 */ 0x14 /* 十六进制 */
浮点型字面值表现为一个小数 或 以科学计数法表示的指数,其中指数部分用 E 或 e 标识。
3.1415926 0. .001 1.6e+05 0e0
默认的,浮点型字面值是一个double
。
字符和字符串字面值
由单引号括起来的一个字符称为char
型字面值,双引号括起来的零个或多个字符则构成字符串型字面值。
'a' //字符字面值
"Hello World!" //宇符串宇面值
字符串字面值的类型实际上是由常量字符构成的数组(array)。编译器会在字符串结尾处添加一个空字符('\0'
),因此,字符串字面值的实际长度要比它的内容多 1。
如果两个字符串字面值位置紧邻且仅由空格、缩进 和 换行符 分隔,则它们实际上是一个整体。当书写的字符串字面值比较长,写在一行里不太合适时,就可以采取分开书写的方式:
std::cout « "a really, really long string literal "
"that spans two lines" << std::endl;
布尔字面值和指针字面值
true 和 false 是布尔类型的字面值;
nullptr 是指针的字面值。
转义序列
作用: 用于表示一些不能显示出来的 ASCII 字符。
制表符\t
: 与前面的字符一共占8个空格,使输出格式整齐。
指定字面值类型
可以通过添加前缀或后缀的形式,改变整型、浮点型和字符型字面值的默认类型。
L'a' // 宽字符型字面值,类型是 wchar_t
u8"hi!" // utf-8 字符串字面值(utf-8用8位编码一个Unicode字符)
42ULL // 无符号整型字面值,类型是 unsigned long long
1E-3F // 单精度浮点型字面值,类型是 float
3.14159L // 扩展精度浮点型字面值,类型是 long double
- 字符和字符串字面值
前缀 | 含义 | 类型 |
---|---|---|
u | Unicode 16 字符 | char16_t |
U | Unicode 32 字符 | char32_t |
L | 宽字符 | wchar_t |
u8 | UTF-8(仅用于字符串字面值) | char |
- 整型字面值
后缀 | 最小匹配类型 |
---|---|
u or U | unsigned |
l or L | long |
ll or LL | long long |
- 浮点型字面值
后缀 | 类型 |
---|---|
f 或 F | float |
l 或 L | long double |