When we use arrays and pointers this way:
int *g() {
int arr[] = {1, 2, 3};
return arr;
}
int f() {
int *value = g();
for (size_t i = 0; i < 3; i ) {
std::cout << value[i] << " ";
}
std::cout << std::endl;
}
int main(int argc, const char **argv) {
f();
return 0;
}
It is deleted from the stack after function g returns, so we get a segment error in the function f()
But using std::vector
this same logic works without any problem:
std::vector<int> g() {
std::vector<int> arr = {1, 2, 3};
return arr;
}
int f() {
std::vector<int> value = g();
for (size_t i = 0; i < value.size(); i ) {
std::cout << value.at(i) << " ";
}
std::cout << std::endl;
}
int main(int argc, const char **argv) {
f();
return 0;
}
I was thinking that this is something related to the fact that we have the std::vector class acting as a container to the array but using a struct as a container delete the array either:
struct T {
int *arr;
};
T g() {
T t;
int arr[] = {1, 2, 3};
t.arr = arr;
return t;
}
int f() {
T value = g();
for (size_t i = 0; i < 3; i ) {
std::cout << value.arr[i] << " ";
}
std::cout << std::endl;
}
int main(int argc, const char **argv) {
f();
return 0;
}
And in this case, g allow us to overflow the array pointer memory limits with the index subscription.
Why vectors doesn't share the same problem?
CodePudding user response:
All the elements in the vector essentially count as part of the vector. And notice you are returning the vector, not a pointer to the vector. But you are returning a pointer to (the start of) the array.
When the function g
returns, the vector arr
is destroyed but that's okay because that's not the same one that is returned. A copy of arr
is returned. (Actually, the compiler is clever enough to reuse the same vector instead of copying it and then deleting one - but you can imagine that it makes a copy)
Then f
prints values from its own local variable value
- no problem.
By contrast, in the pointer version, f
is printing values from the local variable arr
inside g
, which has already been destroyed. If g
would return a pointer to a vector, it would have the same problem.
CodePudding user response:
In the second method returning vector<int>
. The std::vector<int> arr
was copied to another a temporary vector<int>
, then std::vector<int> arr
was destroyed.
Finally, the vector<int> value
was assigned with temporary vector<int>
, after assigning completed, temporary vector<int>
was destroyed as well.
That's what i think happening with your example.
CodePudding user response:
Your intuition was somewhat right it has something to do with the containment of the array inside a struct, what you misunderstand however is the difference between (int[]) and (int*) and probably you don't understand well where memory resides as well.
To clarify:
In C C-style array types (int[]), (char[]) etc cannot be returned from a function. Your function does not return (int[]) it returns int* which is a pointer that points to an an (int[]'s zero element in the stack space of some function f() ) and hence the existence of this (int[]) stops past the lifetime of the function f(), but a dangling pointer was copied to the caller's site;
Try
using int_array = int[4];
int_array f()
{
int_array arr { 1, 2, 3, 4};
return arr; // Does not compile cannot return array
}
int* f2()
{
int_array arr { 1, 2, 3, 4};
return arr; // Compiles because int[4] is implicitly cast to int*
}
In the case of vector, it contains a pointer to a dynamically allocated memory block, acquired through some means(new, malloc, custom user defined allocator, etc) hence this memory block is not bound to the lifetime of a function call, but it is managed by the owner of it. When you return the vector to the caller, the memory block behind continues to exist until a (delete, free, custom-delete) is called on it. The vector does this in it's destructor ( ~vector() ), but before that happening a vector( const vector& ) constructor call occurs that copies the content of one vector to the other. Most compilers will optimize away this operation, as mentioned by another answer.
In the case where you return the struct, the problem is the same, you are not returning an array (int[]) you are returning a pointer. If you were to contain an array inside the struct it would be successfully copied back to caller, because the restriction only applies for arrays and not for user defined types.
Try:
struct Foo {
int arr[4];
};
Foo f()
{
Foo obj{ 1, 2, 3, 4 }; // brace initialization of struct
return obj;
}
int main()
{
Foo obj = f();
for(int i = 0; i < 4; i )
std::cout << i << ' '; // outputs 1 2 3 4 with no issues
}
As you can see no issues with memory here. That's why the std::array<> from header exists - to wrap C-Style arrays so they can be copied over.
I hope this explains well enough.