Changing the starting address of an array

Pages: 12
Is there a use case at all for needing to change the starting address of an array at run time? The last assignment below fails because the code is trying to assign an integer pointer value (the address of some integer variable) to an array of integers. What if the compiler did allow this, and the assignment simply changed the address of the very first element in the array to the value in myPointer -- Is there a use case for that?

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

int main() {
        int myArray [10];
        int * myPointer;

        std::cout << "myPointer: " << myPointer << std::endl;

        myPointer = myArray;
        std::cout << "myPointer: " << myPointer << std::endl;

        myArray = myPointer; // this fails at compile time due to incompatible data types.
}
Last edited on
you cannot do it. ***

there is a use case for treating an array index oddly, negative indexing is supported by c++, but you have to do it with a pointer.

consider a counting sort for signed characters:
unsigned int sorter[259];
unsigned int* sp = &sorter[129]; //this is now the [0] location so the array is -129 to 129-- a little bigger than strictly needed.
sp[-100]++; //the -100 represents a negative character value, 'extended ascii' letters for example.
Similar uses for negative numbers may have merits in a specific problem. These are rather uncommon, but its handy once in a rare while. You need so much documentation / commentary you should think twice before going here, though.

*** ok, its probably possible to trick the compiler and do some sort of screwy undefined behavior, and it may even work. But using normal syntax and well defined code, you cannot.
Last edited on
I'm sure this is bad practice, but I can't work out whether this is either legal or leaks memory.

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

int main() {
        int * myArray = new int[10]{};
        int * myPointer;
//      std::cout << "myPointer: " << myPointer << std::endl;
        myPointer = myArray;
        std::cout << "myPointer: " << myPointer << std::endl;
        myArray = myPointer;   // does myArray lose the "knowledge" that it points to an array of 10 ints here?
        delete [] myArray;     // is this legal?
}
Last edited on
An aside question. What does the "{}" signify in the first line of code inside main()?

I tried the above code regardless, and it does appear that myArray effortlessly gets assigned the value of myPointer, and it looks like it lost prior knowledge of pointing to a 10 int array. Interesting!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

int main() {
                int * myArray = new int[10]{};
                int * myPointer;
                std::cout << "myPointer: " << myPointer << std::endl;
                std::cout << "myArray: " << myArray << std::endl;
                myPointer = myArray;
                std::cout << "myPointer: " << myPointer << std::endl;
                std::cout << "myArray: " << myArray << std::endl;
                myArray = myPointer;   // does myArray lose the "knowledge" that it points to an array of 10 ints here?
                std::cout << "myPointer: " << myPointer << std::endl;
                std::cout << "myArray: " << myArray << std::endl;
                delete [] myArray;     // is this legal?
            }


myPointer: 0
myArray: 0x560ffc1a7eb0
myPointer: 0x560ffc1a7eb0
myArray: 0x560ffc1a7eb0
myPointer: 0x560ffc1a7eb0
myArray: 0x560ffc1a7eb0
Last edited on
> does myArray lose the "knowledge" that it points to an array of 10 ints here?

It never had that "knowledge" to start with.

> is this legal?

It will compile; the program engenders undefined behaviour. (corrected; thanks to dutch)


> What does the "{}" signify n the first line of code inside main()?

Value initialisation https://en.cppreference.com/w/cpp/language/value_initialization
Each element of the array is value-initialized (each int is zero-initialised)
Last edited on
JLBorges wrote:
the program engenders undefined behaviour

How so?
> How so?

I'm sorry, I misread the code; missed line 7: myPointer = myArray;
I stand corrected; thank you!
JLBorges,

Why do you observe that myArray didn't even have the knowledge that it points to an array of 10 integers? Doesn't below line of code initialize myArray to be a pointer to an array of 10 integers, which means there was this pointer had the knowledge to start with?

 
                int * myArray = new int[10]{};
Last edited on
Doesn't below line of code initialize myArray to be a pointer to an array of 10 integers, which means there was this pointer had the knowledge to start with?

Not really, that line of code defines a pointer to int and then allocates memory for 10 ints starting at the address pointed to by the pointer and default initializes those 10 ints. However the pointer only knows that it is pointing to some address in memory.

Do you realize that a pointer and an array are different concepts.
I'm sure this is bad practice, but I can't work out whether this is either legal or leaks memory.
The delete removes any memory leak problem but myPointer is left as a dangling pointer.

Keeping in mind that doesn't matter in such a simple program where the dangling pointer is never used again.

(Re)-Setting myPointer to nullptr is a partial (but incomplete) way of overcoming the dangling pointer.
When you come to do a delete [] how does the code know that [] here means 10 items? Where exactly is "10" stored? (It could have been set at run time.)

Actually, the code compiles without the [], although that clearly wouldn't be a good thing to do.

Last edited on
The code doesn't strictly comply with the new/delete 'structure/syntax' The only reason it works is memory is cleaned up automatically at the end of executing the program - obviously necessary otherwise we'd be up for new RAM after a couple of runs of a program.

delete[] deletes an array vs delete (no brackets) a single new pointer, as you know. The 'size' of the array is defined in the included new[] statement. All that new[] does is allocate enough memory rather than build any limits to being inside or outside an array if that's what you wanted.

delete[] is like 'delete as much memory as was previously allocated'. Probably have to have a look at the source code for new/delete to know exactly what the internals are doing. Maybe there's a delimiting '\0' like C-strings. Whatever, because we can always find out the size of the array with sizeof etc so a delimiter is no big deal to implement the delete[] behind the scenes. i.e. 10 is implicit.

FWIW - smart pointers only dig the hole deeper and I can't be bothered worrying about memory leak detectors so STL containers get another tick if it looks to me like getting too heavy.



> When you come to do a delete [] how does the code know that [] here means 10 items?
> Where exactly is "10" stored?

A common implementation technique is to allocate some extra memory and store additional information there
(this would not be needed if the type involved is trivially destructible).

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

struct A
{
    char filler[3] {} ;

    ~A() { std::cout << "A::destructor\n" ; }

    static void* operator new( std::size_t n )
    {
        std::cout << "A::operator new for non-array: allocate " << n << " bytes\n" ;
        return ::operator new(n) ;
    }

    static void* operator new[] ( std::size_t n )
    {
        std::cout << "A::operator new for array: allocate " << n << " bytes\n" ;
        return ::operator new(n) ;
    }

    // TO DO: operator delete
};

int main()
{
    std::cout << "size of A: " << sizeof(A) << '\n' ; // 3

   new A {} ; // non-array: requests sizeof(A) bytes

   new A[1] {} ; // array: requests additional storage
}


http://coliru.stacked-crooked.com/a/70247c3a804ccad5
https://rextester.com/LAO39458
Not sure that I've got this!

Consider code with two "new" and one "delete". (And no, I don't want to know about memory leak here.)

1
2
3
4
5
6
7
8
9
10
#include <iostream>

int main()
{
   int *A = new int[5]{};
   int *B = new int[10]{};
   
   B = A;
   delete [] B;    // How many ints does this delete? And how does the computer know? 
}


I feel it ought to delete 5 ints (because of the memory that B would be pointing to), but I still don't see how it could tell that. Is there some table somewhere that says that if you point delete [] at some memory location it would deallocate a certain amount of memory?

Now I know why somebody invented vectors!



Just playing. This one suggests 5.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>

struct St
{
   St() { std::cout << "St constructed\n"; }
   ~St() { std::cout << "St destroyed\n"; }
};

int main ()
{
   St *A = new St[5];
   St *B = new St[10];
   
   B = A;
   delete [] B;
   
   std::cout << "Main code finished; anything below is clear-up";
}
Last edited on
Newbie to C++ here.

I ran the above code and it prints on console "St constructed" 15 times and then "St destroyed" 5 times. I'm guessing that's from the instantiation of pointer A to an array of 5 objects of type St, and then of pointer B to an array of 10 objects of type St again. Is my understanding correct?

Separately, I understand the definition below, but I wonder how the line of code St() knows already of a struct St, when it's inside struct St {}, unless St() is simply the constructor (and hence the method name is the same as the class name), and that ~St() is consequently the destructor. Thanks for confirming.
An implementation could be doing something like this:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <new>
#include <algorithm>

struct info
{
    std::size_t cnt = 0 ;
    std::size_t sz = 1 ;
};

struct A
{
    char filler[1] {} ;

    A() {  std::cout << this << " A::constructor\n" ; }
    ~A() { std::cout << this << " A::destructor\n" ; }
};

static_assert( alignof(A) == 1U ) ; // for brevity of this example

A* array_new_A( std::size_t n )
{
    // allocate storage for info + n objects of type A
    void* pv = operator new[] ( sizeof(info) + n * sizeof(A) ) ;

    info* pi = static_cast<info*>(pv) ;
    *pi = { n, sizeof(A) } ;

    A* pa = reinterpret_cast<A*>(pi+1) ;

    std::cout << "construct " << n << " objects\n" ;
    for( std::size_t i = 0 ; i < n ; ++i ) ::new(pa+i) A{} ;

    return pa ;
}

void array_delete_A( A* pa )
{
    // from pa, get to the info for this array of objects
    info* pi = reinterpret_cast<info*>(pa) - 1 ;

    std::size_t n = pi->cnt ;
    std::cout << "destroy " << n << " objects\n" ;
    while( n > 0 ) { pa[--n].~A() ; }

    operator delete[](pi) ;
}

int main()
{
    std::size_t n1 ;
    std::size_t n2 ;
    while( std::cout << "how many objects each? " && std::cin >> n1 >> n2 )
    {
        A* p1 = array_new_A(n1) ;
        A* p2 = array_new_A(n2) ;

        using std::swap ; swap(p1,p2) ;

        array_delete_A(p2) ; // n1 objects
        array_delete_A(p1) ; // n2 objects
    }
}
The CRT memory manager knows how many bytes et al has been allocated. It's been a source of frustration for many years that this info isn't provided as a c/c++ api. There are some ad-hoc functions on some systems that give some info - but nothing standard.

1
2
3
4
5
6
7
8
9
10
#include <iostream>

int main()
{
   int *A = new int[5]{};
   int *B = new int[10]{};
   
   B = A;
   delete [] B;    // How many ints does this delete? And how does the computer know? 
}


A is allocated memory for 5 ints
B is allocated memory for 10 ints

B is set to the address for 5 ints. The memory used for the 10 ints is now unknown to the code (but is known to the CRT memory manager),
B is freed memory for 5 ints

The code doesn't explicitly free the memory for 10 ints.

Depending upon the underlying OS (if any), when the process closes any still allocated memory may be freed (10 ints in this case). This shouldn't be relied upon. Windows does this as part of it's process destruction.
Last edited on
Thank-you.

1
2
3
4
5
    void* pv = operator new[] ( sizeof(info) + n * sizeof(A) ) 
....
    A* pa = reinterpret_cast<A*>(pi+1) ;
....
    return pa ;


Would I then be able to determine the size of the allocated memory block by hunting through a few memory locations before that pointed to? (I have tried - but without success. Probably because I don't know what form "info" would take for a particular implementation.)
Last edited on
> Probably because I don't know what form "info" would take for a particular implementation.

Yes. And also because the implementation would have to take care of the alignment requirement of the type involved. A debugger which comes with the implementation could be written to give us this information quite easily.

There is no requirement in the standard that what is done must be something akin to this. For instance, a conforming implementation could maintain a hash table, with the pointer as the key and the associated information as the mapped data.
Pages: 12