0
int main(){
  int * x = new int[3];

  for(int i = 0; i < 5; ++i){
      x = new int[i];
      x[i] = i;
      cout << i << endl;
  }
}

So say I have an integer array allocated on the heap, with capacity of 3 integers, as seen in x.

Now say I have this for loop where I want to change the values of x into whatever i is.

Now when I run this code by it self it does run, and 0,1,2,3,4 prints like I want.

What I'm wondering is, when I do new int[i] when i is 0, 1, 2, since x[0], x[1], x[2] is already allocated on the heap, am I make three new address in the heap?

Thanks!

Saxtheowl
  • 4,136
  • 5
  • 23
  • 32
ming
  • 229
  • 1
  • 9
  • 4
    Your code has undefined behavior and memory leaks. You always allocate less memory in the loop than you use and allocating zero bytes of memory is also not ok. – Sami Kuhmonen Oct 28 '19 at 04:27
  • I'm not sure I fully understand your question, but the code you have shown is a memory leak. –  Oct 28 '19 at 04:28
  • 2
    `std::vector` would be a big help for you here. – Mark Ransom Oct 28 '19 at 04:32

4 Answers4

1
int main(){
  int * x = new int[3];

  for(int i = 0; i < 5; ++i){
      x = new int[i];
      x[i] = i;
      cout << i << endl;
  }
}
Running it
-> An array of size 3 is created on the heap 
-> An array of size 0 is created on the heap
-> The 0 index of array size 0 is equal to 0
-> Print 0
-> An array of size 1 is created on the heap
-> The 1 index of array size 1 is equal to 1
-> Print 1
.
.
.
-> An array of size 4 is created on the heap
-> The 4 index of array size 4 is equal to 4
-> Print 4

I'm not sure if this is your intention, but as the rest of the comments said, there are memory leaks and undefined behavior.

I think what you're trying to implement is instead

#include <iostream> 
#include <vector>  

int main() 
{ 
    std::vector<int> g1; //initialises a vector called g1
    for (int i = 1; i <= 5; i++) {
        // Adds i to the vector, expands the vector if 
        // there is not enough space
        g1.push_back(i);
        // Prints out i 
        std::cout<<i<<std::endl;
    }
Dumb chimp
  • 444
  • 1
  • 4
  • 13
1

Every time you new something, you are requesting memory and system is allocating a memory block(it may fail, that's another case itself) and giving you a pointer to the initial address of that memory, please remember this when you new something. So here initially you new an array of 3 ints, then in the loop you again new 5 times which returns 5 new memory addresses(which is 5 different memory blocks). So you have 6 new addresses(memory blocks of different sizes) to deal with. It's definitely not the thing you want. So you should use the 1st allocation without any more new in the loop, in that case you should know the bounds of array beforehand. So to make that automatic you can use vector which can grow when you push elements into it. please refer to this for vector.

a sidenote: when you new something you should take care of that memory yourself, so new is generally not inspired, please look at smart pointers to make your code safe.

quidstone
  • 55
  • 1
  • 8
1

Can you allocate memory for an array already on the heap?

Answer: Yes (but not how you are doing it...)

Whenever you have memory already allocated, in order to expand or reduce the allocation size making up a given block of memory, you must (1) allocate a new block of memory of the desired size, and (2) copy the existing block to the newly allocated block (up to the size of the newly allocated block), before (3) freeing the original block. In essence since there is no equivalent to realloc in C++, you simply have to do it yourself.

In your example, beginning with an allocation size of 3-int, you can enter your for loop and create a temporary block to hold 1-int (one more than the loop index) and copy the number of existing bytes in x that will fit in your new tmp block to tmp. You can then delete[] x; and assign the beginning address of the new temporary block of memory to x (e.g. x = tmp;)

A short example continuing from your post could be:

#include <iostream>
#include <cstring>

int main (void) {

    int nelem = 3,                  /* var to track no. of elements allocated */
        *x = new int[nelem];        /* initial allocation of 3 int - for fun */

    for (int i = 0; i < 5; i++) {

        nelem = i + 1;                          /* update nelem */

        /* create temporary block to hold nelem int */
        int *tmp = new int[nelem];              /* allocate tmp for new x */
        memcpy (tmp, x, i * sizeof *tmp);       /* copy i elements to tmp */
        delete[] x;                             /* free x */

        x = tmp;                                /* assign tmp to x */
        x[i] = i;                               /* assign x[i] */

        for (int j = 0; j < nelem; j++)         /* output all */
            std::cout << "  " << x[j];
        std::cout  << '\n';
    }

    delete[] x;     /* free x */
}

(note: on the first iteration zero bytes are copied from x -- which is fine. You can include an if (i) before the memcpy if you like)

Example Use/Output

$ ./bin/allocrealloc
  0
  0  1
  0  1  2
  0  1  2  3
  0  1  2  3  4

Memory Use/Error Check

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ valgrind ./bin/allocrealloc
==6202== Memcheck, a memory error detector
==6202== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6202== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==6202== Command: ./bin/allocrealloc
==6202==
  0
  0  1
  0  1  2
  0  1  2  3
  0  1  2  3  4
==6202==
==6202== HEAP SUMMARY:
==6202==     in use at exit: 0 bytes in 0 blocks
==6202==   total heap usage: 7 allocs, 7 frees, 72,776 bytes allocated
==6202==
==6202== All heap blocks were freed -- no leaks are possible
==6202==
==6202== For counts of detected and suppressed errors, rerun with: -v
==6202== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Note that there is such a thing as `realloc`, but I believe its use is discouraged in C++ in favor of `std::vectors` instead. –  Oct 28 '19 at 18:09
  • 1
    @Chipster, yes agree on both points, include `cstring` and you have C `realloc`, and yes `new/delete` are discouraged in favor of standard containers, but there is also 20+ years of C++ codebase that use `new/delete` so understanding it's use remains important. – David C. Rankin Oct 28 '19 at 19:16
0

I'll just throw your code back at you with some comments that hopefully clear things up a bit.

int main()
{
  // Allocate memory for three integers on the heap.
  int * x = new int[3];

  for(int i = 0; i < 5; ++i)
  {
      // Allocate memory for i (0-4) integers on the heap.
      // This will overwrite the x variable allocated earlier.
      // What happens when i is zero?
      x = new int[i];
      // Set the recently allocated array at x[i] to its index.
      x[i] = i;
      // Print out current index of 0-4.
      cout << i << endl;
  }
}
Pickle Rick
  • 808
  • 3
  • 6