Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

(C++) What happened to an array allocated on the stack when the function is finished?

I come from many years of development in Java, and now that I want switch to C++ I have hard times understanding the memory management system.

Let me explain the situation with a small example:

From my understanding, you can allocate space either on the stack or on the heap. The first is done by declaring a variable like this:

 int a[5]

or

int size = 10;
int a[size]

On the contrary, if you want to allocate memory on the heap, then you can do it using the "new" command. For example like:

int *a = new int[10]; (notice that I haven't tried all the code, so the syntax might be wrong)

One difference between the two is that if it is allocated on the stack when the function is finished then the space is automatically deallocated, while on the other case we must explicitly deallocate it with delete().

Now, suppose I have a class like this:

class A {
  const int *elements[10];

  public void method(const int** elements) {
    int subarray[10];
    //do something
    elements[0] = subarray;
  }
}

Now, I have few questions:

  1. in this case, subarray is allocated on the stack. Why after the function method has finished, if I look on elements[0] I still see the data of subarray? Has the compiler translated the first allocation in a heap allocation (and in this case, is this a good practice)?
  2. if I declare subarray as "const", then the compiler does not let me assign it to elements. Why not? I thought that the const only concerns the inability to change the pointer, but nothing else.
  3. (this is probably quite stupid) suppose I want to allocate "elements" not with a fixed 10 elements, but with a parameter that comes from the constructor. Is it still possible to allocate it in the stack, or the constructor will always allocate it in the heap?

Sorry for such questions (that might look silly to a expert C programmer), but the memory management system of C++ is VERY different from Java, and I want to avoid leakings or slow code. Many thanks in advance!

like image 458
jrbn Avatar asked Oct 01 '13 16:10

jrbn


People also ask

Are arrays allocated on stack in C?

Unlike Java, C++ arrays can be allocated on the stack.

Where is an array allocated in C?

Array bucket values are stored in contiguous memory locations (thus pointer arithmetic can be used to iterate over the bucket values), and 2D arrays are allocated in row-major order (i.e. the memory layout is all the values in row 0 first, followed by the values in row1, followed by values in row 2 ...).

Is memory freed when a function ends?

No. When you exit this function, the block of memory which was allocated by malloc remains allocated. The variable p, which, in this example is the only reference to that memory, is gone, so the memory cannot be found to be freed.

How are arrays allocated?

To allocate memory for an array, just multiply the size of each array element by the array dimension. For example: pw = malloc(10 * sizeof(widget));


1 Answers

a) in this case, subarray is allocated on the stack. Why after the function method has finished, if I look on elements[0] I still see the data of subarray? Has the compiler translated the first allocation in a heap allocation (and in this case, is this a good practice)?

It's called "undefined behavior" and anything can happen. In this case, the values that subarray held are still there, incidentally, probably because you access that memory immediately after the function returns. But your compiler could also zero-out those values before returning. Your compiler could also send fire-spewing dragons to your home. Anything can happen in "undefined behavior"-land.

b) if I declare subarray as "const", then the compiler does not let me assign it to elements. Why not? I thought that the const only concerns the inability to change the pointer, but nothing else.

This is a rather unfortunate quirk of the language. Consider

const int * p1; // 1
int const * p2; // 2
int * const p3; // 3
int * p4;       // 4
int const * const p5; // 5

This is all valid C++ syntax. 1 says that we have a mutable pointer to a const int. 2 says the same as 1 (this is the quirk). 3 says that we have a const pointer to a mutable int. 4 says that we have a plain old mutable pointer to a mutable int. 5 says that we have a const pointer to a const int. The rule is roughly this: Read const from right-to-left, except for the very last const, which can either be on the right or on the left.

c) suppose I want to allocate "elements" not with a fixed 10 elements, but with a parameter that comes from the constructor. Is it still possible to allocate it in the stack, or the constructor will always allocate it in the heap?

If you need dynamic allocation, then this will usually be on the heap, but the notion of stack and heap is implementation-dependent (i.e. whatever your compiler vendor does).

Lastly, if you have a Java background, then you'll need to consider ownership of memory. For example, in your method void A::method(const int**), you point your pointers to locally created memory, while that memory goes away after the method returns. Your pointers now point to memory that nobody owns. It would be better to actually copy that memory into a new area (for example, a data member of the class A), and then let your pointers point to that piece of memory. Also, while C++ can do pointers, it would be wise to avoid them at all costs. For example, strive to use references instead of pointers when possible and appropriate, and use the std::vector class for arbitrary sized arrays. This class'll also take care of the ownership problem, as assigning a vector to another vector will actually copy all the elements from the one to the other (except now with rvalue references, but forget that for the moment). Some people regard a "naked" new/delete as bad programming practice.

like image 123
rwols Avatar answered Sep 30 '22 00:09

rwols