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.
}
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.
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 = newint[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?
}
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 = newint[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?
}
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?
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.
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.
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).
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 = newint[5]{};
int *B = newint[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";
}
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.
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 = newint[5]{};
int *B = newint[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.
void* pv = operatornew[] ( 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.)
> 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.