Cpp学习21 std::array、C风格数组

铁名_IronName Lv5
最佳实践

对于 constexpr 数组,请使用 std::array; 对于非 constexpr 数组,请使用 std::vector 。

1
std::array<int, 5> a {};  // a std::array of 5 ints

非常量变量和运行时常量不能用于长度。

std::array 是一个聚合类型,使用聚合初始化进行初始化。
如果初始化列表中提供的初始化器数量少于定义的数组长度,则剩余的未初始化元素将进行值初始化。

尽可能将 std::array 定义为 constexpr。如果 std::array 不是 constexpr,请考虑改用 std::vector 。

std::array 的长度和索引

std::array 的长度和索引的类型为 size_type ,它始终为 std::size_t
获取长度的方法有 arr.size(), std::size() 和 std::ssize() 三种。

为了在存在 constexpr 索引时进行编译时边界检查,我们可以使用 std::get() 函数模板,该模板接受索引作为非类型模板参数。

1
std::cout << std::get<3>(prime);

传递和返回 std::array

1
2
3
4
5
6
7
template <typename T, std::size_t N> // note that this template parameter declaration matches the one for std::array
void passByRef(const std::array<T, N>& arr)
{
static_assert(N != 0); // fail if this is a zero-length std::array

std::cout << arr[0] << '\n';
}

自动非类型模板参数 C++20,略。所以说,能写类型的地方都可以考虑auto啊。

当数组不大或者对性能没要求时,可以按值返回std::array;
用 输出参数 :通过非常量引用or地址传入数组,然后在函数里修改。

类类型的 std::array 和大括号省略

std::array 的元素可以是任何对象类型,包括复合类型。(这么强?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct House
{
int number{};
int stories{};
int roomsPerStory{};
};

int main()
{
constexpr std::array houses { // use CTAD to deduce template arguments <House, 3>
House{ 13, 1, 7 },
House{ 14, 2, 5 },
House{ 15, 2, 4 }
};

简写(省略元素类型名,增加一层大括号):

1
2
3
4
5
6
7
constexpr std::array<House, 3> houses { // initializer for houses
{ // extra set of braces to initialize the C-style array member with implementation_defined_name
{ 13, 4, 30 }, // initializer for array element 0
{ 14, 3, 10 }, // initializer for array element 1
{ 15, 3, 40 }, // initializer for array element 2
}
};

通过 std::reference_wrapper 处理引用数组

1
2
3
4
5
int x { 1 };
int y { 2 };
int z { 3 };

std::array<std::reference_wrapper<int>, 3> arr { x, y, z };

 std::ref() 和 std::cref() 函数作为快捷方式,用于创建 std::reference_wrapper 和 const std::reference_wrapper 包装的对象。请注意,这些函数可以与 auto 一起使用,从而避免显式指定模板参数。

std::array 和枚举

基于范围的 for 循环不允许您遍历枚举的枚举值,最直接的解决方案之一是创建一个包含所有枚举值的 constexpr std::array ,然后遍历该数组。

C 风格数组

1
int testScore[30] {};

应用于 C 风格的数组时, sizeof() 返回整个数组使用的字节数。
std::size() 和 std::ssize() 都是可以用的。

C 风格的数组不支持赋值。
赋值要求左操作数是可修改的左值,而 C 风格的数组不被认为是可修改的左值。

1
2
3
int arr[] { 1, 2, 3 }; // okay: initialization is fine
arr[0] = 4; // assignment to individual elements is fine
arr = { 5, 6, 7 }; // compile error: array assignment not valid

C 风格的数组退化

当在表达式中使用 C 风格的数组时,数组会被隐式转换为指向元素类型的指针,并用第一个元素的地址(索引为 0)进行初始化。通俗地说,这被称为数组衰减 (或简称衰减 )。

1
2
3
4
void printElementZero(const int arr[]) // treated the same as const int*
{
std::cout << arr[0];
}
最佳实践

尽量避免使用 C 风格的数组。

  • 对于只读字符串(字符串字面量、符号常量和字符串参数),建议使用 std::string_view 。
  • 对于可修改的字符串,建议使用 std::string 。
  • 对于非全局 constexpr 数组,建议使用 std::array 。
  • 对于非条件数组,建议使用 std::vector 。

对于全局 constexpr 数组,使用 C 风格的数组是可以的。我们稍后会讨论这一点。

在现代 C++ 中,C 风格的数组通常在以下两种情况下使用:

  1. 用于存储 constexpr 全局(或 constexpr 静态局部)程序数据。由于此类数组可直接从程序中的任何位置访问,因此无需传递数组本身,从而避免了与数据衰减相关的问题。定义 C 风格数组的语法比 std::array 更简洁。更重要的是,此类数组的索引不存在像标准库容器类那样的符号转换问题。
  2. 对于需要直接处理 non-constexpr C 风格字符串参数(而不是需要转换为 std::string_view )的函数或类,这样做有两个可能的原因:
    1. 首先,将 non-constexpr C 风格字符串转换为 std::string_view 需要遍历该 C 风格字符串以确定其长度。如果该函数位于性能关键代码段,并且不需要长度信息(例如,因为该函数无论如何都会遍历字符串),那么避免转换可能很有用。
    2. 其次,如果该函数(或类)调用其他需要 C 风格字符串的函数,那么仅仅为了转换而转换为 std::string_view 可能并非最佳选择(除非您有其他需要使用 std::string_view 原因)。

指针运算和下标

给定一个指针 ptr , ptr + 1 返回内存中 下一个对象 的地址(基于指针指向的类型)。

下标运算 ptr[n] 的简洁语法等价于更冗长的表达式 *((ptr) + (n)) 。
 *(ptr-1)返回内存中的 前一个对象 , 等效于 ptr[-1]。  

最佳实践

从数组开头(元素 0)开始索引时,最好使用下标,这样数组索引就可以与元素对齐。

从给定元素进行相对定位时,优先使用指针运算。

C-style strings

1
2
3
4
char str1[8]{};                    // an array of 8 char, indices 0 through 7

const char str2[]{ "string" }; // an array of 7 char, indices 0 through 6
constexpr char str3[] { "hello" }; // an array of 6 const char, indices 0 through 5

输出 C 风格字符串时, std::cout 会一直输出字符,直到遇到空终止符为止。空终止符标记字符串的结尾,因此即使是丢失了长度信息的字符串也能被打印出来。

在现代 C++ 中,存储用户输入的文本时,使用 std::string 更安全,因为 std::string 会自动调整以容纳所需的字符数。

std::strlen() 速度很慢,因为它必须遍历整个数组,计算字符数,直到遇到空终止符为止。

C 风格的字符串符号常量

在现代 C++ 中,几乎没有理由使用 C 风格的字符串符号常量。相反,应该优先使用 constexpr std::string_view 对象,它们通常速度一样快(甚至更快),而且行为更加一致。

多维 C 风格数组

数组的维度是指选择元素所需的索引数量。仅包含一个维度的数组称为一维数组 (有时缩写为 1d 数组 )。
数组的数组称为二维数组 (有时缩写为 2d 数组 ),因为它有两个下标。

1
2
3
4
5
6
int array[3][5]
{
{ 1, 2 }, // row 0 = 1, 2, 0, 0, 0
{ 6, 7, 8 }, // row 1 = 6, 7, 8, 0, 0
{ 11, 12, 13, 14 } // row 2 = 11, 12, 13, 14, 0
};

不允许省略非最左侧的尺寸

多维 std::array

把 std::array 嵌套起来。

1
2
3
4
5
std::array<std::array<int, 4>, 3> arr {{  // note double braces
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
}};

对应的模版函数

1
2
3
4
5
6
7
8
9
10
11
template <typename T, std::size_t Row, std::size_t Col>
void printArray(const std::array<std::array<T, Col>, Row> &arr)
{
for (const auto& arow: arr) // get each array row
{
for (const auto& e: arow) // get each element of the row
std::cout << e << ' ';

std::cout << '\n';
}
}

很显然这个类型名太长了,可以用别名模板来简化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <array>
#include <iostream>

// An alias template for a two-dimensional std::array
template <typename T, std::size_t Row, std::size_t Col>
using Array2d = std::array<std::array<T, Col>, Row>;

// When using Array2d as a function parameter, we need to respecify the template parameters
template <typename T, std::size_t Row, std::size_t Col>
void printArray(const Array2d<T, Row, Col> &arr)
{
for (const auto& arow: arr) // get each array row
{
for (const auto& e: arow) // get each element of the row
std::cout << e << ' ';

std::cout << '\n';
}
}

int main()
{
// Define a two-dimensional array of int with 3 rows and 4 columns
Array2d<int, 3, 4> arr {{
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }}};

printArray(arr);

return 0;
}

三维的:

1
2
template <typename T, std::size_t Row, std::size_t Col, std::size_t Depth>
using Array3d = std::array<std::array<std::array<T, Depth>, Col>, Row>;

用函数模板获取长度

1
2
3
4
5
6
7
8
9
10
11
12
13
// Fetch the number of rows from the Row non-type template parameter
template <typename T, std::size_t Row, std::size_t Col>
constexpr int rowLength(const Array2d<T, Row, Col>&) // you can return std::size_t if you prefer
{
return Row;
}

// Fetch the number of cols from the Col non-type template parameter
template <typename T, std::size_t Row, std::size_t Col>
constexpr int colLength(const Array2d<T, Row, Col>&) // you can return std::size_t if you prefer
{
return Col;
}

std::mdspan 是在 C++23 中引入的,它是一个可修改的视图,为连续的元素序列提供了一个多维数组接口。

  • 标题: Cpp学习21 std::array、C风格数组
  • 作者: 铁名_IronName
  • 创建于 : 2026-08-13 13:41:13
  • 更新于 : 2026-08-13 16:54:29
  • 链接: https://blog.ironname.top/2026/Cpp/Cpp学习21/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论