Getting Unexpected output in C++

Viewed 41

I am trying to create a function which creates an array and return a pointer of the array: Here's my code:

#include<iostream>

using namespace std;

int* example() {
    int arr[] = {1,2,3};
    int *a = arr;
    return a;
}

int main() {
    int *a = example();
    cout << *a     << endl;
    cout << *(a+1) << endl;
    cout << *(a+2) << endl;
    return 0;
}

And here's the output:

1
1878006336
3

I don't know why am I getting that garbage value at 2nd line instead of 2.

Here are some of my observations regarding this:

  1. I am getting desired output if I manually create the array and pointer in the main() function.
  2. If I print the second line before the first line, I get desired output as:
2
1
3

What is the reason behind this weird behaviour?

3 Answers

The array arr is declared local to the function. As a result its lifetime ends when the function exits. We call this automatic lifetime. Returning a pointer to this memory invokes undefined behavior. The code might work the way you expect, or it might not.

To work around this, you need a lifetime that is not automatic, and which can be controlled. There are a few paths to this.

Manual memory management

You might use dynamic memory management with new and delete (or in this case delete []). This is generally discouraged as it creates many opportunities for mistakes.

int *example() {
    int *a = new int[3] { 1, 2, 3 };

    return a;
}

You would need to remember to delete this array.

int main() {
    int *a = example();

    cout << a[0] << endl;
    cout << a[1] << endl;
    cout << a[2] << endl;

    delete[] a;

    return 0;
}

Smart pointers

You could use a smart pointer, which makes explicitly deallocating unnecessary, as the memory it points to is deallocated when the smart pointer goes out of scope.

unique_ptr<int[]> example() {
    auto a = unique_ptr<int[]>(new int[3] { 1, 2, 3 });

    return a;
}

int main() {
    auto a = example();

    cout << a[0] << endl;
    cout << a[1] << endl;
    cout << a[2] << endl;

    return 0;
}

STL containers

The most idiomatic approach is to use an STL container class.

The std::array container maps directly to the fixed size arrays seen so far. The std::vector container is more appropriate if the size is unknown at compile time, which it often will be, making this a very common container to see in idiomatic C++ code.

As with the smart pointer, the memory does not have automatic storage duration, so it can live beyond the scope in which it's declared, and the data is deallocated when the container goes out of scope.

array<int, 3> example() {
    array<int, 3> a = { 1, 2, 3 };

    return a;
}

int main() {
    auto a = example();

    cout << a[0] << endl;
    cout << a[1] << endl;
    cout << a[2] << endl;

    return 0;
}

Your function is allocating array in the local scope/stack, it won't be available for the outside caller. Allocate the array using new operator for C++, and malloc() for C, and then that pointer will be valid for the caller function.

arr[] is a local variable, it is stored on the stack, its content might not be preserved after the example() function has returned.

Here are 2 examples that can get the behavior that you want:

Example 1: Store array in global region

#include<iostream>

using namespace std;

int arr[] = {1,2,3};

int* example(){
    int* a = arr;
    return a;
}

int main(){
    int* a = example();
    cout<<*a<<endl;
    cout<<*(a+1)<<endl;
    cout<<*(a+2)<<endl;
    return 0;
}

Example 2: Store array in heap region

#include<iostream>
#include<cstdlib.h>

using namespace std;

int* example(){
    int* arr = malloc(3 * sizeof(int));
    arr[0] = 1;
    arr[1] = 2;
    arr[2] = 3;
    int* a = arr;
    return a;
}

int main(){
    int* a = example();
    cout<<*a<<endl;
    cout<<*(a+1)<<endl;
    cout<<*(a+2)<<endl;
    free(a);
    return 0;
}
Related