Skip to content
0

C++数据类型

基础数据类型

注意:以下数据类型的位数以64位操作系统为准。

基础数据类型JavaC#C++
布尔类型bool
8位字符类型xchar(取值范围:[-128, 127]或[0, 255])
8位有符号字符类型signed char(取值范围:[-128, 127])
8位无符号字符类型unsigned char(取值范围:[0, 255])
16位Unicode字符类型charchar16_t
32位Unicode字符类型xchar32_t
宽字符类型xwchar_t(2或4字节,存储Unicode字符)
8位有符号整数bytesbytebyte
16位有符号整数short
32位有符号整数int
64位有符号整数longlong [long]
32位单精度浮点类型float
64位双精度浮点类型double
扩展精度浮点类型xlong double(8、12或16字节)
精准浮点类型xdecimalx
8位无符号整数sbyteunsigned byte
16位无符号整数ushortunsigned short
32位无符号整数uintunsigned int
64位无符号整数ulongunsigned long [long]
  • Java没有有无符号之分,都是有符号的,取值范围从负数到正数。
  • C++的long类型可能是4字节或8字节,具体取决于平台,但long long一定是8字节。

复合数据类型

数组

  • 数组名表示数组首元素的地址,而&数组名表示整个数组的首地址。虽然值相同,但含义不同。
  • 数组名可看做是一个指向不变的指针(相关内容见const关键字)。
c++
int arr1[3] = {1, 2, 3};
cout << "arr1  = " << arr1 << endl;
cout << "&arr1 = " << &arr1 << endl;
cout << "arr1 + 1 = " << arr1 + 1 << endl;  // 第二个元素地址,相差4个字节(int类型)
cout << "&arr1 + 1 = " << &arr1 + 1 << endl;  // 相差整个数组长度的字节数,即3*4字节
cout << "*(arr1 + 1) = " << *(arr1 + 1) << endl;  // 第二个元素的值

定义数组的长度必须是编译时常量。常见的编译时常量:

字符数组

字符数组是C风格的字符串。

  • 字符数组末尾需要有结束标识符号'\0'。
  • 字符数组是可以修改的。
c++
// 系统自动在末尾加上'\0',所以数组长度为4
char name1[] = "dog";  
// {}形式初始化,系统不会在末尾加上'\0',需要自己加上。
char name2[] = { 'd', 'o', 'g', '\0'};  
// 字符串长度小于数组长度,数组剩余空间全被初始化为'\0'
char name3[10] = "dog";
cout << name1 << " " << strlen(name1) << " " << sizeof(name1) << endl; // 3 3 4 
cout << name2 << " " << strlen(name2) << " " << sizeof(name2) << endl; // 3 3 4 
cout << name3 << " " << strlen(name3) << " " << sizeof(name3) << endl; // 3 3 10

name3[3] = 's';
name3[5] = 'x';
cout << "modified name3 = " << name3 << endl;  // dogs

提示

在获取了字符串的长度后,切记长度+1来初始化字符数组的长度!

指针

指针是一个变量,其存储的是值的地址,而不是值本身。

c++
int x = 10;
int* ptr = &x;
int** pptr = &ptr;  // 指针是变量,所以它也有地址,pptr是二级指针,即指向指针的指针。
// &x表示x的地址,即ptr变量的值。
cout << "&x = " << x << ", ptr = " << ptr << endl;
// *运算符用于指针表示解除引用,可以获取指针所指向的地址空间的值。即x = *ptr。
cout << "x = " << x << ", *ptr = " << *ptr << endl;

指针多用于使用new在堆中动态分配内存,如:

c++
int* p1 = new int(10);
int* p2 = new int[10];
// do something;
delete p1; // 释放内存
delete[] p2;  // 使用new[]为数组分配内存,则应使用delete[]释放内存。
  • new分配的内存用delete来释放;new[]为数组分配的内存用delete[]来释放。
  • 不要使用delete来释放不是new分配的内存
  • 不要使用delete释放同一内存块两次。
  • 对空指针应用delete是安全的

常见指针问题

  • 野指针:未被初始化或已经被释放的指针,其指向的内存地址是未知的。声明指针时要初始化为nullptr或有效的地址。
  • 悬挂指针:指针指向的内存空间已经被释放,但指针仍然指向该内存空间。新手常见的错误是函数返回了局部变量(栈空间)的指针。
c++
static int* buildX(int n=1)
{
  int x = 10 * n; // 在栈空间上分配的内存,函数执行完之后将被释放。
  cout << "&x=" << &x << endl;
  return &x;
}
static int* buildY(int n=1)
{
  int* y = new int(10 * n);  // 在堆空间上分配的内存,由程序员自己释放。
  cout << "y=" << y << endl;
  return y;
}
int main()
{
  int* xp = buildX(10); // xp是悬挂指针,所指向的内存空间已经被释放
  cout << "xp=" << xp << ", *xp=" << *xp << endl;
  int* yp = buildY(10);
  cout << "yp=" << yp << ", *yp=" << *yp << endl;
  delete yp;  // 释放指针所指向的内存空间,但指针本身的值还是指向该内存空间。
  yp = nullptr;  // 避免悬挂指针
}

指向函数的指针

最简形式:void (*p_func)()

较为复杂的函数指针数组:

c++
const double* (*pf[3])(const double* arr, int n) = { func1, func2, func3 };

const double* (*(*pf2)[3])(const double* arr, int n) = &pf;
解读

首先要知道运算符的优先级:() > [] > *

引用【左值引用】

引用是已定义的变量的别名,其主要用途是作为函数的形参。通过将引用变量用作参数,函数将使用原始数据,而不是其副本,提高效率。

c++
int a = 10;
int &ra = a;  // ra是a的引用
int* pa = &a; // pa是a的指针

提示

必须在声明引用变量时进行初始化,并且不能重新绑定到另一个变量。

枚举

C++11引入了增强型枚举,枚举值限定在类作用域内,避免了命名冲突。

c++
enum class State : int
{
  Unknown = 0,
  Success = 1,
  Failed = 2,
  Running = 3
};

auto state = State::Running;

尽量少用传统枚举类型,但有些场合挺好用,比如在类内定义编译时常量:

c++
class Stack
{
public:
  enum { MAX_SIZE = 100 };
  // static constexpr int MAX_SIZE = 100;
  int arr[MAX_SIZE];
}

结构体

C++的结构体和类基本相同,唯一的区别是默认的访问权限:结构体成员和继承的默认访问权限都是public;而类成员和继承的默认访问权限都是private。

结构体主要用于对数据的简单封装,较为复杂的业务逻辑应该使用类。

c++
struct Point
{
    float x;
    float y;

    void display() const
    {
        cout << "(" << x << ", " << y << ")" << endl;
    }
};

void test_struct()
{
    Point point = {1.0, 2.5};
    point.display();
}

联合体

多个成员共享同一块内存。

标准库数据类型

string

string类是对字符数组的封装,支持自动扩容,提供了更加简单的API。

pair

std::pair<T1, T2>是一个模板类,适用于需要将两个元素(可以不同类型)组合在一起的场景。

c++
#include <utility>
using namespace std;

std::pair<int, char> tuple(1, 'a');
cout << "first val:" << tuple.first << endl;
cout << "second val:" << tuple.second << endl;

tuple

元组是pair的泛化,支持任意数量的元素组合。用法比较奇怪,用的时候去查吧!

容器

容器是存储其它对象的对象,储存的对象类型必须是可复制构造的和可赋值的。

序列容器

基本容器描述内存结构固定大小使用场景
array固定数组连续存储数组大小固定的随机索引访问
vector动态数组连续存储动态扩容且随机索引访问
list双向链表非连续存储双向遍历,随机插入或删除元素
forward_list单向链表非连续存储单向遍历,随机插入或删除元素
deque双端队列分段连续内存头尾两端频繁插入或删除元素

容器适配器是基于基本容器的简化接口,它们提供了一种特定的接口来访问底层容器的数据,其底层容器通常可以有多种选择。

容器适配器描述特点默认底层容器
stack后进先出deque
queue队列先进先出deque
priority_queue优先级队列快速访问优先级最高的元素vector实现的最大堆

关联容器

基于键值对的容器,它们通过键来存储和访问元素。关联容器分为集合和映射,集合使用红黑树(对数级时间复杂度),映射使用哈希表(常数级时间复杂度)。

关联容器描述
set有序集合
unordered_set无序集合
multipset允许重复值的有序集合
unordered_multiset允许重复值的无序集合
map有序映射
unordered_map无序映射
multipmap同一个键可关联多个值的有序映射
unordered_multimap同一个键可关联多个值的无序映射

数据类型转换

类型转换描述
静态类型转换static_cast将一种数据类型的值强制转换为另一种近似的数据类型的值。
动态类型转换dynamic_cast通常用于将一个基类指针或引用转换为派生类指针或引用。指针类型转换失败返回空指针;引用类型转换失败会抛出std::bad_cast异常
常量转换
const_cast
将const类型的对象转换为非const类型的对象,不改变对象类型。
重新解释转换
reinterpret_cast
将一个数据类型的值重新解释为另一个数据类型的值,通常用于在不同的数据类型之间进行转换。