Why does new int[x] disappear after leaving func scope?

Sep 17, 2019 at 10:39am
I want to check if an int pointer parameter is nullptr and if so, I want to create an int array, fill it with data and the finish the function.
It works up until I leave the scope of func(intPtr) then the intPtr address is set to 00000000. How do I get the new int array to exist out in the main function?

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

class Foo
{
    public:
Foo(){x = 5;};
void func(int* intPtr)
{
   if(intPtr == nullptr)
   {
      intPtr = new int[x]{6};
   }
   else
   {
      intPtr[0] = x;
   }
}

private:
int x;
};

int main()
{
   int* intPtr = nullptr;
   Foo bar;
   bar.func(intPtr);
   std::cout << "asdf" << intPtr[0] << std::endl;
   return 0;
}
Sep 17, 2019 at 10:52am
You have to pass a pointer to a pointer to function func() to allocate the array inside funcion func(). Because this is brainfuck, consider using std::vector.

1
2
3
4
5
6
7
8
std::vector<int> func() {
    return std::vector<int>(6);
}

int main() {
  std::vector<int> v = func();
  std::cout << v[0] << "\n";
}
Sep 17, 2019 at 10:56am
You are sending a COPY of intPtr to the function. The COPY then gets updated to point at the new array, and then the COPY is destroyed at the end of the function. The original intPtr is untouched.

Don't send a COPY; pass by reference so the function works on the original intPtr:

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

class Foo
{
    public:
Foo(){x = 5;};
void func(int*& intPtr) // pass the pointer BY REFERENCE
{
   if(intPtr == nullptr)
   {
      intPtr = new int[x]{6};
   }
   else
   {
      intPtr[0] = x;
   }
}

private:
int x;
};

int main()
{
   int* intPtr = nullptr;
   Foo bar;
   bar.func(intPtr);
   std::cout << "asdf" << intPtr[0] << std::endl;
   return 0;
}


Now, that done, here are some other notes to just be aware of; in modern C++, as a very strong rule, don't use arrays, don't do manual memory management (i.e. don't use new or malloc).

How do I get the new int array to exist out in the main function?

The new array you created exists forever (or at least until the program ends). This is a memory leak; you've created an array using new, and then your (original) code loses the pointer to it. The memory is still allocated. The array still exists. You just lost the pointer to it.
Sep 17, 2019 at 12:35pm
Hello neguse,

To go along with what Repeater has said these links may help understanding scope:
http://www.cplusplus.com/doc/tutorial/namespaces/

Starting here https://www.learncpp.com/ scroll down to chapter "S". You may find several chapters worth reading. The section that I believe fits here best is https://www.learncpp.com/cpp-tutorial/4-3a-scope-duration-and-linkage-summary/
The next chapter on "Namespaces" is also worth reading.

To be fair another option is to have the function return an int pointer, i.e., int* func(). Doing this there is no need for any parameters. In "main" the line would be intPtr = bar.func();.

To avoid rewriting the program passing the pointer by reference is the best and quickest option.

Hope that helps,

Andy

EDIT:
Last edited on Sep 17, 2019 at 12:36pm
Sep 17, 2019 at 12:46pm
Thank you for you help both of you, but I gave you a bad explanation and wrong code, I apologise.
I have updated below.

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
33
34
35
36
37
38
#include <iostream>
#include <string>
#include <assert.h>

class Foo
{
    public:
Foo(){x = 5;};
int func(int*& pRefInt) //How to pass original address in here?
{
   if(pRefInt== nullptr)
   {
      pRefInt= new int[x]{6};
   }
   else
   {
      pRefInt [0] = x;
   }
   return pRefInt[0];
}

private:
int x;
};

int FuncInMain(int* intPtr)
{
 Foo bar;
 return bar.func(intPtr); //How to pass original address in here?
}

int main()
{
   int* mustBePtr= nullptr; //Original address

   assert(FuncInMain(mustBePtr) == 6);
   return 0;
}
Sep 17, 2019 at 12:51pm

You are now sending a COPY of mustBePtr to the function FuncInMain. The COPY then gets passed to func (not another copy; you're passing by reference to func so func does not receive a copy), and then the COPY is updated in func, and then the COPY is destroyed at the end of FuncInMain.

This is basically the exact same problem.

1
2
3
4
int FuncInMain(int* intPtr)  // Receives a COPY of the int-pointer
{
   // SOME CODE
}


1
2
3
4
int FuncInMain(int*& intPtr)  // Receives the actual int-pointer
{
   // SOME CODE
}


See the difference? We already showed you this once.
Last edited on Sep 17, 2019 at 12:53pm
Sep 17, 2019 at 1:02pm
Ok, so if I understand it correctly, the problem is that FuncInMain() must take an int pointer as argument (which is a requirement for the task), but then there is no way of getting the original address of mustBePtr. Is that correct?

The possible caes I m looking at is if mustBePtr is a nullptr it should be populated (is that the right word?) with an int array.
Last edited on Sep 17, 2019 at 1:05pm
Sep 17, 2019 at 1:16pm
Why not practice the whole by-value vs by-reference thing with integers first, so at least you understand the concept.
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
#include <iostream>
using namespace std;

// This won't change the value in the caller
void foo ( int a ) {
  cout << "foo::a=" << a << endl;
  a = 2;
  cout << "foo::a(new)=" << a << endl;
}

// This will change the value in the caller
void bar ( int &a ) {
  cout << "bar::a=" << a << endl;
  a = 2;
  cout << "bar::a(new)=" << a << endl;
}

int main ( ) {
  int v = 42;
  cout << "main=" << v << endl;
  foo(v);
  cout << "main after foo=" << v << endl;
  bar(v);
  cout << "main after bar=" << v << endl;
}
Sep 17, 2019 at 1:22pm
(which is a requirement for the task)


Tell us the task. Exactly as written.
Sep 17, 2019 at 1:35pm
Just to beat the dead horse...
pointers are nothing special. if you pass one to a function by value, when that function ends and you pop back to where you were, its unchanged.

so
void foo(int x)
{
x = 3;
}
int y = 11;
foo(y);
//what is y here? its 11.

if you put an & on it in foo (int &x), then y is 3 instead.

pointers work the same way.

void foo(int *ip) //pass by value
void foo(int *&ip)//pass by reference. this is what you want. yes, its weird looking. you MUST understand that the TYPE is {int*} and its a reference to one of those. Its CRITICAL to understand this.

and beyond that, its ALSO CRITICAL to know that you can change what is pointed to off a value passed pointer.
that is:
void foo(int *ip) //passed by value
{
ip[0] = 1234; //this change will remain after the function ends. The pointer was passed by value. the memory where it points is a free for all area and change it anywhere it stays changed.
}

Last edited on Sep 17, 2019 at 1:38pm
Sep 17, 2019 at 1:57pm
I am to write the following function for finding a path:

int FromStartToGoal(const Vector2 mapSize, const Vector2 start, const Vector2 goal, const unsigned char* grid, int* mustBePtrBuf, const int buffersize).

The function returns the amount of steps to reach the goal, grid is a char array mustBePtrBuf is an array with all indices you passed and buffesize is the max amount of steps allowed in mustBePtrBuf.

Everything is wet up and works perfectly, I just had a thought "what if the user sends in a nullptr? Should I create a new array for them?".
Sep 17, 2019 at 2:03pm
> I just had a thought "what if the user sends in a nullptr? Should I create a new array for them?".
Or just return an error value telling them that it can't be nullptr.

The problem with allocating things for someone unexpectedly is that you then push the burden of freeing that memory onto the caller. Maybe they don't want that (or expect it), and you end up with memory leaks.

Sep 17, 2019 at 2:23pm
This is why std::vector exist, to not think about memory leaks. Your function calculate the indices from start to goal. This it the result. Return is from the function.

 
std::vector<int> FromStartToGoal(const Vector2 mapSize, const Vector2 start, const Vector2 goal, const unsigned char* grid).
Last edited on Sep 17, 2019 at 2:24pm
Sep 17, 2019 at 2:44pm
I just had a thought "what if the user sends in a nullptr?

what is the requirement and design here is the question. Don't go rogue and fix things your own way without consulting the requirements and designs. Perhaps you should stop the program here (fatal, unexpected null and no way to resolve the issue). Maybe you should do nothing, and warn the user. Maybe you should create it for them. Maybe something else is needed. There is no way to tell you what to do here, because we don't know what is needed for your code.

if you need to create it for them, they need to delete it, or you can wrap your pointers in a dumb pointer class (self destruct when going out of scope) or use one of the advanced C++ pointers (I don't know which is best here; I have not spent time on new pointer types yet, since I know how to use the old ones).

Try to avoid dynamic memory. Use the tools that do it for you if you can. If you must use it, be sure you design carefully who owns what memory (responsible for delete) or use some self-cleaning strategy.
Last edited on Sep 17, 2019 at 2:49pm
Sep 17, 2019 at 3:33pm
Thank you everyone for all your help. It has been a great help, I have learned lots of things apart fomr how to handle pointers ad references. I will keep memory leaks in mind in particular.
Topic archived. No new replies allowed.