概要
std::array
是C++
标准库提供的一个封装原生定长数组的模板类
std::array<T, N>
与原生数组T[N]
具有相同的内存大小,并且默认都是分配在栈内存std::array
相比原生数组多了更多的可操作性和属性, 比如获取数组大小可以直接使用array.size()
, 整数数组清零可以使用array.fill(0)
std::array
也支持原生数组相同的for each
循环- 总之就是,原生数组能做的事,
std::array
都可以做,std::array
可以做的事,原生数组不一定可以做 - 建议优先使用
std::array
使用方法
包含头文件
#include <array>
定义与初始化
- 局部变量
// 使用传统默认构造: 每个元素都会调用默认构造
std::array<int, 100> numbers; // C++11前的语法
std::array<int, 100> numbers{}; // C++11后的语法
// 使用 auto
auto numbers = std::array<int, 10>{}; // C++11开始支持
- 类成员变量
class Dog {
public:
class Leg { /* 内容忽略 */};
private:
std::array<Leg, 4> legs_ {};
};
使用场景
建议使用的场景
数量固定的相同元素作为一个整体时
不建议使用的场景
一次分配超大空间,使用局部内存加辅助长度信息存储长度不固定的元素。这种场景,应该使用支持动态扩容的 std::vector
字节数组妙用
字节数组
表示数组的元素大小只有一个字节, 比如 std::array<char, 8>
, std::array<uint8_t, 256>
, std::array<std::byte, 1024>
等等
有何妙用?
可以比较方便的操作内存块的任意字节
虽然 std::array
默认定义时,使用的时栈内存,但不妨碍我们使用堆内存或者其他内存,所以std::array
所使用的内存属于什么类型,完全取决于我们的初始化方式.。比如:
- 函数内定义的局部普通实例, 使用的是栈内存
int func()
{
std::array<uint8_t, 32> batch; // 占内存,建议不要分配过大导致栈溢出
return 0;
}
- 静态变量, 包括
函数内静态变量
,全局变量
,类静态变量
, 使用的是静态内存,地址和大小都是固定的 new
,malloc
,智能指针
等创建的实例,使用的都是堆内存实例 + 一个栈内存指针- 直接定义指针,可以指向任意内存块
static std::array<char, 1024> large_buffer;
void func()
{
std::array<char, 16> small_buffer;
auto p1 = new std::array<char, 8>{}; // 指向分配的堆内存
auto p2 = &large_buffer; // 指向静态内存
auto p3 = &small_buffer; // 指向栈内存
}
- 甚至可以指向一个带有实际类型的内存块,可以你方便的操作该内存块的任意字节
struct Info { /* 内容忽略 */};
void func()
{
Info info;
auto p = reinterpret_cast<std::array<char, sizeof(Info)> *>(&info); // 此时的p所指向的内存块与 `info`变量完全一致, 可以操作p的任意字节,修改的就是info对应的内存
std::cout << p->size() << std::endl; // 显示info结构体的大小
(*p)[0] = 0xff; // 将info结构体的第一个字节改为 0xff
std::cout << (*p)[3] << std::endl; // 显示info结构体的第四个字节
// 如果能确保生命周期的安全性,甚至可以直接定义为引用
auto& ref = *reinterpret_cast<std::array<char, sizeof(Info)> *>(&info);
ref[0] = 0; // 对info结构体的第一个字节清0
ref.fill(0); // 对整个info结构体清零
for (auto byte: ref) {
// 遍历info结构体的每一个字节
}
}