我对C ++很新,一直在避免使用指针。从我在网上看到的,我不能返回一个数组,但我可以返回一个指向它的指针。我做了一个小代码来测试它,并想知道这是否是正常/正确的方法:
#include <iostream>
using namespace std;
int* test (int in[5]) {
int* out = in;
return out;
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int* pArr = test(arr);
for (int i = 0; i < 5; i++) cout<<pArr[i]<<endl;
cout<<endl;
return 0;
}
修改:这似乎不太好。我该怎么改写呢?
int* test (int a[5], int b[5]) {
int c[5];
for (int i = 0; i < 5; i++) c[i] = a[i]+b[i];
int* out = c;
return out;
}
答案 0 :(得分:16)
您现在的代码是正确的,但我很难弄清楚它是如何在现实世界中使用的。话虽如此,请注意从函数返回指针时的一些注意事项:
int arr[5];
的数组时,它会在堆栈上分配并且是函数的本地数据。arr
传递给test()
。std::unique_ptr
/ std::shared_ptr<>
上阅读。编辑 - 回答矩阵乘法的用例
您有两种选择。天真的方式是使用std::unique_ptr
/ std::shared_ptr<>
。 Modern C ++的方法是让Matrix
类重载operator *
,如果你想避免复制乘法的结果,你必须使用新的rvalue references
。功能。除了copy constructor
,operator =
和destructor
之外,您还需要move constructor
和move assignment operator
。浏览this search的问题和答案,以获得有关如何实现这一目标的更多信息。
编辑2 - 回答附加问题
int* test (int a[5], int b[5]) {
int *c = new int[5];
for (int i = 0; i < 5; i++) c[i] = a[i]+b[i];
return c;
}
如果您将其用作int *res = test(a,b);
,那么稍后在代码中,您应该调用delete []res
来释放test()
函数中分配的内存。您现在看到的问题是,手动跟踪何时调用delete
非常困难。因此,在答案中概述了如何处理它的方法。
答案 1 :(得分:6)
您的代码没问题。请注意,如果返回指向数组的指针,并且该数组超出范围,则不应再使用该指针。例如:
int* test (void)
{
int out[5];
return out;
}
上述内容永远不会有效,因为out
返回时test()
不再存在。不能再使用返回的指针。如果你做使用它,你将阅读/写入你不应该的内存。
在原始代码中,arr
数组在main()
返回时超出范围。显然这没问题,因为从main()
返回也意味着你的程序正在终止。
如果你想要的东西会一直存在并且不能超出范围,你应该用new
分配它:
int* test (void)
{
int* out = new int[5];
return out;
}
返回的指针始终有效。请记住,使用delete[]
:
int* array = test();
// ...
// Done with the array.
delete[] array;
删除它是回收它使用的内存的唯一方法。
答案 2 :(得分:2)
新问题的新答案:
您无法从函数返回指向自动变量(int c[5]
)的指针。自动变量以返回封闭块结束其生命周期(在本例中为函数) - 因此您将返回指向不存在的数组的指针。
让变量动态化:
int* test (int a[5], int b[5]) {
int* c = new int[5];
for (int i = 0; i < 5; i++) c[i] = a[i]+b[i];
return c;
}
或者更改您的实施以使用std::array
:
std::array<int,5> test (const std::array<int,5>& a, const std::array<int,5>& b)
{
std::array<int,5> c;
for (int i = 0; i < 5; i++) c[i] = a[i]+b[i];
return c;
}
如果您的编译器没有提供std::array
,您可以用包含数组的简单结构替换它:
struct array_int_5 {
int data[5];
int& operator [](int i) { return data[i]; }
int operator const [](int i) { return data[i]; }
};
旧问题的旧答案:
你的代码是正确的,......嗯,好吧,......没用。因为数组可以分配给没有额外功能的指针(注意你已经在函数中使用了它):
int arr[5] = {1, 2, 3, 4, 5};
//int* pArr = test(arr);
int* pArr = arr;
更多功能签名:
int* test (int in[5])
相当于:
int* test (int* in)
所以你认为没有意义。
但是这个签名需要一个数组,而不是指针:
int* test (int (&in)[5])
答案 3 :(得分:1)
引用数组的变量基本上是指向其第一个元素的指针,所以是的,您可以合法地返回指向数组的指针,因为 thery本质上是相同的东西。自己检查一下:
#include <assert.h>
int main() {
int a[] = {1, 2, 3, 4, 5};
int* pArr = a;
int* pFirstElem = &(a[0]);
assert(a == pArr);
assert(a == pFirstElem);
return 0;
}
这也意味着将数组传递给函数应该通过指针(而不是通过int in[5]
)来完成,并且可能还有数组的长度:
int* test(int* in, int len) {
int* out = in;
return out;
}
那就是说,使用指针(没有完全理解它们)是对的,这是非常危险的。例如,引用在堆栈上分配并超出范围的数组会产生未定义的行为:
#include <iostream>
using namespace std;
int main() {
int* pArr = 0;
{
int a[] = {1, 2, 3, 4, 5};
pArr = a; // or test(a) if you wish
}
// a[] went out of scope here, but pArr holds a pointer to it
// all bets are off, this can output "1", output 1st chapter
// of "Romeo and Juliet", crash the program or destroy the
// universe
cout << pArr[0] << endl; // WRONG!
return 0;
}
因此,如果您感觉不够用力,请使用std::vector
。
[回答更新的问题]
编写test
函数的正确方法是:
void test(int* a, int* b, int* c, int len) {
for (int i = 0; i < len; ++i) c[i] = a[i] + b[i];
}
...
int main() {
int a[5] = {...}, b[5] = {...}, c[5] = {};
test(a, b, c, 5);
// c now holds the result
}
或者这(使用std::vector
):
#include <vector>
vector<int> test(const vector<int>& a, const vector<int>& b) {
vector<int> result(a.size());
for (int i = 0; i < a.size(); ++i) {
result[i] = a[i] + b[i];
}
return result; // copy will be elided
}
答案 4 :(得分:0)
在真实应用中,返回数组的方式称为,使用out参数。当然你实际上不必返回指向数组的指针,因为调用者已经拥有它,你只需要填充数组。传递另一个指定数组大小的参数也是很常见的,以免溢出它。
使用out参数的缺点是调用者可能不知道数组需要多大才能存储结果。在这种情况下,您可以返回std :: vector或类似的数组类实例。
答案 5 :(得分:0)
您的代码(看起来没问题)不会返回指向数组的指针。它返回一个指向 数组的第一个元素的指针。
事实上,这通常是你想要做的。大多数数组操作都是通过指向各个元素的指针完成的,而不是通过指向整个数组的指针来完成的。
你可以定义一个指向数组的指针,例如:
double (*p)[42];
将p
定义为指向double
的42个元素数组的指针。一个很大的问题是你必须指定数组中元素的数量作为类型的一部分 - 并且该数字必须是编译时常量。大多数处理数组的程序都需要处理不同大小的数组;给定数组的大小在创建后不会发生变化,但其初始大小在编译时不一定是已知的,不同的数组对象可以有不同的大小。
指向数组第一个元素的指针允许您使用指针算法或索引运算符[]
来遍历数组的元素。 但是指针不会告诉你数组有多少元素;你通常必须自己跟踪。
如果函数需要创建数组并返回指向其第一个元素的指针,则必须以多种方式之一自行管理该数组的存储。您可以让调用者传入指向数组对象(的第一个元素)的指针,可能还有指定其大小的另一个参数 - 这意味着调用者必须知道数组需要多大。或者函数可以返回指向函数内部定义的静态数组(的第一个元素)的指针 - 这意味着数组的大小是固定的,同一个数组将被第二次调用函数破坏。或者函数可以在堆上分配数组 - 这使得调用者负责以后解除分配。
到目前为止我写的所有东西都是C和C ++的共同点,事实上它在C语言中比C ++更具风格。 comp.lang.c FAQ的第6节讨论了C中数组和指针的行为。
但如果你是用C ++编写的,那么最好使用C ++习语。例如,C ++标准库提供了许多定义容器类的头文件,例如<vector>
和<array>
,它们将为您处理大部分内容。除非你有特殊的理由使用原始数组和指针,否则你最好只使用C ++容器。
编辑:我认为您在输入此答案时编辑了您的问题。问题末尾的新代码就像你观察者一样,没有任何好处;它返回一个指向对象的指针,该对象在函数返回后立即停止存在。我想我已经涵盖了其他选择。
答案 6 :(得分:0)
你可以(有点)返回一个数组
而不是
int m1[5] = {1, 2, 3, 4, 5};
int m2[5] = {6, 7, 8, 9, 10};
int* m3 = test(m1, m2);
写
struct mystruct
{
int arr[5];
};
int m1[5] = {1, 2, 3, 4, 5};
int m2[5] = {6, 7, 8, 9, 10};
mystruct m3 = test(m1,m2);
测试看起来像
struct mystruct test(int m1[5], int m2[5])
{
struct mystruct s;
for (int i = 0; i < 5; ++i ) s.arr[i]=m1[i]+m2[i];
return s;
}
效率不高,因为复制它会传递数组的副本