CPP函数如何安全返回数组

因为数组不能拷贝,所以函数不能返回数组,不过,函数可以返回数组的指针或引用。在现代 C++ 中,我们应该优先使用标准库容器而不是 C 风格的原始数组和指针,比如std::arraystd::vector

方法一:C 风格的语法(不推荐,但需要了解)

C 风格数组指针的返回类型语法非常晦涩。

1. 直接声明(最复杂)

语法为:元素类型 (*函数名())[数组大小]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

// 函数 get_arr 返回一个指针,该指针指向一个包含 5 个 int 的数组
int (*get_arr())[5] {
static int arr[5] = {1, 2, 3, 4, 5};
return &arr; // 返回整个数组的地址
}

int main() {
int (*ptr_to_arr)[5] = get_arr(); // 接收指针

// 解引用指针以获取数组本身,然后遍历
for (int i = 0; i < 5; ++i) {
std::cout << (*ptr_to_arr)[i] << " "; // 注意 (*ptr_to_arr) 的括号
}
std::cout << std::endl; // 输出: 1 2 3 4 5
}

语法解析

  • get_arr():表示 get_arr 是一个函数。
  • *get_arr():表示函数返回一个指针。
  • (*get_arr())[5]:括号确保 *get_arr() 先结合,说明返回的是一个指向大小为 5 的数组的指针。
  • int (*get_arr())[5]:这个数组的元素类型是 int

2. 使用类型别名 (usingtypedef)(推荐的 C 风格写法)

为了简化复杂的语法,可以使用类型别名,让代码可读性大大提高。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

// C++11 using 语法 (更推荐)
using Array_t = int[5];

// C-style typedef 语法
// typedef int Array_t[5];

Array_t* get_arr_aliased() {
static int arr[5] = {10, 20, 30, 40, 50};
return &arr;
}

int main() {
Array_t* ptr_to_arr = get_arr_aliased();
for (int i = 0; i < 5; ++i) {
std::cout << (*ptr_to_arr)[i] << " ";
}
std::cout << std::endl; // 输出: 10 20 30 40 50
}

这种方法清晰地将复杂的类型定义与函数签名分开,是 C 风格中最好的选择。

3. 使用尾返回类型 (auto->) (C++11)

这是另一种简化语法的方式,让返回类型的书写更符合阅读顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

// auto 作为占位符,真正的返回类型在 -> 后面
auto get_arr_trailing() -> int (*)[5] {
static int arr[5] = {11, 22, 33, 44, 55};
return &arr;
}

int main() {
auto ptr_to_arr = get_arr_trailing(); // auto 自动推断类型
for (int i = 0; i < 5; ++i) {
std::cout << (*ptr_to_arr)[i] << " ";
}
std::cout << std::endl; // 输出: 11 22 33 44 55
}

方法二:现代 C++ 的方法(强烈推荐)

在现代 C++ 中,我们应该优先使用标准库容器而不是 C 风格的原始数组和指针。

1. 返回 std::array (大小固定的数组)

std::array 是对 C 风格数组的封装,类型安全,有关联的大小信息,并且可以轻松地按值或按引用返回。

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
#include <iostream>
#include <array>

// 直接按值返回,编译器通常会用 RVO (返回值优化) 避免拷贝
std::array<int, 5> get_std_array() {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
return arr;
}

// 或者返回引用 (如果数据源生命周期足够长)
std::array<int, 5>& get_std_array_ref() {
static std::array<int, 5> arr = {10, 20, 30, 40, 50};
return arr;
}

int main() {
auto my_arr = get_std_array(); // 简单、安全
for (int val : my_arr) {
std::cout << val << " ";
}
std::cout << std::endl;

auto& my_arr_ref = get_std_array_ref();
for (int val : my_arr_ref) {
std::cout << val << " ";
}
std::cout << std::endl;
}

2. 返回 std::vector (大小可变的数组)

如果数组大小是动态的,std::vector 是最佳选择。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <vector>

std::vector<int> get_vector() {
return {100, 200, 300}; // 使用列表初始化直接返回
}

int main() {
std::vector<int> my_vec = get_vector();
for (int val : my_vec) {
std::cout << val << " ";
}
std::cout << std::endl;
}

总结

方法 优点 缺点/适用场景 推荐度
C 风格 - 直接声明 C/C++ 兼容 语法极其晦涩,易错 不推荐
C 风格 - 类型别名 极大提高可读性 仍是 C 风格,不含大小信息,不安全 在必须用 C 风格数组时推荐
C 风格 - 尾返回 提高可读性 C++11+ 限定,本质仍是 C 风格指针 在必须用 C 风格数组时推荐
现代 C++ - std::array 类型安全,语法简单,自带大小 大小需在编译期确定 强烈推荐 (固定大小)
现代 C++ - std::vector 类型安全,语法简单,大小动态 有堆内存分配开销 强烈推荐 (动态大小)

结论:除非有特殊理由(如与 C 库交互),否则请始终优先使用 std::arraystd::vector