Please help clear my confusion about pointers/stack/heap


Hi,

I am trying to understand pointers and meanwhile trying to choose which naming convention to choose. (int* pointer / int *pointer)

I leaned most of the time to *pointer but now I learn that you can only assign memory addresses to a pointer and that values are basically assumed to be addresses, I think I lean more to the int* naming convention to not be confused or deluded to think you are working with values as you would while dereferencing with *pointer.

Okay so while this change of mind occurred I thought; what does the new keyword return?
Okay it returns an address.

 
className* objectName = new className;


^ This makes sense.

But what if you place it in a variable that is not a pointer?

 
className objectName = new className;


Why would you initialise a variable that is not a pointer, and thus on the stack, with an address? Does the memory address that the new creates depend on what kind of variable it is assigned too?

I.e. if it is a className* objectName that it is being assigned to, the memory address will be on the heap.
And if it is a className object, that the new keyword is being assigned to, it gives a memory address on the stack? Or does it put the instance of that object in the memory address that is already in the stack where the identifier points to?

In the latter case the new keyword doesn't really return an memory address, right? It would kind of read the address of the variable and puts the object in it.

What do I need to learn? 🙂
Last edited on
Why not int * pointer? XD...

Alright to your question:

Are you sure that it would even compile? Have you tried?
Last edited on
to understand pointers, think of them like this: pointers are just an array index into ram. You have to talk to the OS to get and release memory if you are pointing to new memory and not something you already had in hand.

and example?
int x[5]; //x is like ram
int index = 3; //index is like a pointer
x[index] = 1234; //this is like a pointer deref and assignment.

so ram already exists ... but
int*index = new int; //same as index = 3, this assigns an offset into that big array we call ram, it could be 3, it could be 1298753 just as easily.
*index = 1234; //same as x[index] = 1234, it just has odd syntax.

note that in both cases changing index takes you somewhere else in the data.
note that in both cases changing the value pointed to does not change the index
the index (pointer!) and the data are distinct, in other words.

I advise you to play with some raw pointers in a main program without any classes at all. just an int pointer, first just 1 int, then an array of them. I just told you how it works, but the syntax is a little different than you are used to, and it takes a little playing to get through it.

once you understand that...
yes, you can take a pointer's numeric value as an integer. You can do all kinds of ugly things like this, but you probably should not.
eg
int * ip = new int;
uint64_t x = (uint64_t)(ip);
char * cp;
cp = (char*)(x);
this is roundabout (you can just copy pointer to pointer with casting without the middleman) but it will work if you mess with forcing it via casting (and maybe i messed it up, but it CAN be done if you persist in pushing the issue against the warnings and complaints).

now, why would you take a pointer to a stack variable?
the above is a clue.
lets say you wanted the third byte of a 32 bit integer for a standard hash/crc/encryption/etc type gibberish code.

int32_t x = 1234;
unsigned char* cp = (unsigned char*)(&x);
cp[3] ^= 255;
you just modified the value of x, by tampering with one of its bytes, which is common in these types of low level algorithms... it avoids nonsense like shifting a copy of x down with an and and all that extra computation and then rearranging that back to put into x via another logical statement... try it; try writing the above with logical statements and see how many you need to do this simple thing. Then consider how much work the direct access really does (the pointer isnt real, the compiler will probably figure out that its an offset into the current register and convert char* cp into an xor of an 8 bit slice of a register and do all this on the cpu in 2 or 3 instructions)

I think I will stop here and let you ask a better question.
Last edited on
But what if you place it in a variable that is not a pointer?

className objectName = new className;


You wouldn't. In many cases, that would be illegal. It would only be legal if there were some way to convert a pointer (which is, basically, an integer), to an object of type className. But, even if you'd written such a conversion (by creating a constructor), I can't think of a case where that would ever be useful to do.

new here returns a pointer, just like in your first example.

Why would you initialise a variable that is not a pointer, and thus on the stack, with an address?


That bolded part suggests you've misunderstood something. Just because a variable is a pointer, doesn't mean it's not on the stack. Just because a variable is not a pointer, doesn't mean it's on the stack.

A pointer is just a variable like any other. If it's a local variable, it's on the stack. If it's a global variable, or a static variable, it's on the heap.

The value of a pointer is a memory address; i.e. it describes a location in memory. We say the pointer is pointing to a memory location as a shorthand for that.

That location may be on the stack, or on the heap, or it could be nowhere (if you've set the pointer to nullptr or NULL or 0), or it could be unknown (if you haven't initialised the pointer).

The crucial thing to remember is that the location of the pointer variable is unrelated to the location of the memory it's pointing to.

So:

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

int gI;    // gI is a global variable.  It is on the heap
int* gPtr; // gPtr is a global variable.  It is on the heap

int main()
{
  int i = 0;  // i is a local variable.  It is on the stack.
  int *ptr;   // ptr is a local variable.  It is on the stack.

  ptr = &i;    // ptr is still on the stack.  It is pointing to memory on the stack,
               // because i is on the stack.
  ptr = &gI;   // ptr is still on the stack.  It is pointing to memory on the heap,
               // because gI is on the heap.
  ptr = new int[10].  // ptr is still on the stack.  It is pointing to memory on the heap,
                      // because dynamically allocated memory is on the heap.

  gPtr = &i;   // gPtr is still on the heap.  It is pointing to memory on the stack,
               // because i is on the stack.
  gPtr = &gI;  // gPtr is still on the heap.  It is pointing to memory on the heap,
               // because gI is on the heap.
  gPtr = new int[10]. // gPtr is still on the heap.  It is pointing to memory on the 
                      // heap, because dynamically allocated memory is on the heap.

  delete[] gPtr ;  // Because we always clean up after ourselves.

  gPtr = ptr;  // gPtr is still on the heap.  It is pointing to memory on the 
               // heap, because it is pointing to the same memory as ptr is pointing to,
               // and ptr is pointing to dynamically allocated memory on the heap.
               // ptr is still on the stack.

  delete ptr;  // Note that "delete gPtr" would do exactly the same thing, because
               // gPtr and iPtr are pointing to the same memory.

} 
Last edited on
Regarding naming conventions, I wouldn't use int*. That makes it too easy to make this mistake:
int* p1, p2; // declare two pointers!
But it doesn't declare two pointers. p1 is a pointer but p2 is an integer. If you do it the other way, it's much clearer:
int val, *p, &r; // declare an int, a pointer to int, and a reference to int.

That location may be on the stack, or on the heap, or it could be nowhere

A word on terminology vs what is taught, as well.
stack and heap are just concepts.
your program launches, and it is allocated a pile of memory, where the program's instructions, static data, etc all live and then past this, it has free memory that it owns and uses for its variables. All this is in RAM. ALL of it. Part, usually all, of the free memory is what you are calling the 'stack'. But its all just a section of your ram, and because of that, every bit of it (including the instructions, routine entry points (you will come to know these as function pointers) has an address and can be poked at via a pointer.
And there is the heap. this is just "free available ram" which by definition is not part of the memory given to your program (as that is not free anymore, your program owns it).

so when you make a pointer as a local variable
*you get an integer on the stack*
which contains the address to another location in ram.
that could be inside your program's memory blocks, or, if using new, outside of it.



Last edited on
so when you make a pointer,
*you get an integer on the stack*

That's only true if you choose to create the pointer on the stack. You can just as easily create the pointer on the heap - as shown in my example code - in which case *you get an integer on the heap* .

The important thing is that a pointer is just a variable, like any other. It's just a variable that holds an integer.

The rules of scope, persistence, memory management etc apply to it in exactly the same way that they apply to any other variable.

You can create them on the stack, or on the heap, just like any other variable.
Last edited on
Agreed: Globals and a couple of other items do go on the heap (but inside the memory the program owns, just not the stack part, its treated much like instructions and static data). I have corrected the post to reflect this detail.

For what its worth, I would try to get past the whole stack/heap thing. I honestly feel it is not a huge issue anymore. Others may disagree, but where something lives is not generally useful and about the only time you think about it when you run out of stack and need to move your data out to the heap (the OS is happy to tell you this during testing); and today's c++ uses containers that do just that. I can't think of the last time I had a stack blowout. Apart from stack blowout, I can't recall ever caring if something was stack or heap outside of a classroom.

Is that even what you were trying to understand? I read it and got 'why make a pointer to a stack variable' (which has a lot of reasons, I gave a single simple one but there are dozens) and he read it as 'why put the pointer itself on the heap' which I don't think matters one way or the other in the grand scheme.

For what its worth, I would try to get past the whole stack/heap thing. I honestly feel it is not a huge issue anymore. Others may disagree, but where something lives is not generally useful and about the only time you think about it when you run out of stack and need to move your data out to the heap (the OS is happy to tell you this during testing); and today's c++ uses containers that do just that. I can't think of the last time I had a stack blowout. Apart from stack blowout, I can't recall ever caring if something was stack or heap outside of a classroom.

I broadly agree. I suspect that, for beginners, "stack" and "heap" serve as convenient, if inaccurate, shorthand for "memory that is freed up when an object (or variable) goes out of scope" and "memory that stays allocated regardless of scope", which definitely is a useful concept for beginners to get their heads around.

Is that even what you were trying to understand?

Is that last paragraph directed at me, or the OP?
The OP. I think you have a handle on it. (pun intended).
And, of course, this is going to turn into one of those threads where several people put in a lot of time and effort explaining things to the OP, but the OP never bothers to come back and read it, isn't it?
At least others with the same/similar questions will benefit from this discussion, if they bother reading it.
Topic archived. No new replies allowed.