I am having this piece of code:
try
{
int* myTestArray = new int[2];
myTestArray[4] = 54;
cout << \"Should throw ex \" << myTestAr
As others have said, this is undefined behavior, but I thought a bit more info might help. myTestArray
is not an "Array" in the sense of a type, with special operators, etc. It is just a pointer to a location in memory. The expression myTestArray[4]
is just short-hand for *(myTestArray+4)
- it is returning a reference to the memory location that is 4 * sizeof(int)
past myTestArray
. If you want bounds checking, you'll have to use std::vector<int>::at()
.
Accessing unallocated memory is not guaranteed to throw exceptions.
It's actually not guaranteed to do anything, since that's undefined behavior. Anything could happen. Beware of nasal demons.
It prints 55 because you just stored 54, fetched it back and then printed 54+1. It's not at all guaranteed to print 55, although that's often what will happen in practice. This time it worked.
There is an unstated, and incorrect, assumptions here. That assumption is that C++ actually gives a damn about what you do with memory. C++, like its C ancestor, has a completely unchecked model of memory. What you have here is classically called a buffer overflow, and is a source of innumerable bugs including some horrible security flaws.
Here's what your code really says:
myTestArray
is the name of a location in memory big enough to hold the address of an int
.
Two int
s worth of memory have been allocated on the heap for it. [And that addreress is put into the location myTestArray
. Doesn't matter, but that probably makes it clearer.] (Along with probably 16 bytes of overhead, but we don't care about that now.)
you then are sticking the value 54
into the memory location 4 int
s from the address contained in myTestArray
.
looking at that location, adding 1 and printing the result.
You are demonstrating that C(++) indeed just doesn't care.
Now, under most conditions the underlying memory management and run time system won't let you get away with it; you will violate it's assumptions and get a segmentation error or something similar. But in this case, you are not hitting a boundary yet, most likely because you're piddling on the data structure that malloc is using under the covers to manage the heap. You're getting away with it because nothing is happening with the heap for the rest of the program. But for a real good time, write a little loop that does this code, freeing myTestArray
and reallocating it. I'd lay long odds it won't run for more than 10 iterations before the program blows up, and might not make two.
Knowing what's going on here for sure is very hard to do. But I can give you a rough idea.
Most operating systems have a minimum size for memory allocations. In Unix it is the native page size. On x86 and amd64 systems this is 4 kB. In Windows it is 64 kB (I think).
The memory allocator used by malloc
and new
gets memory from the operating system in chunks of this size. It sets up data structures (often a linked list, sometimes a bitmap, or a tree) and hands out small pieces of the requested sizes.
One other confusing thing is that before your program even starts running main()
it has run quite a bit of other code and allocated memory. For std::cout
and other static and global objects, and for shared library linking.
But assume that when you call new
your program first gets a chunk of 4 kB and gives you a pointer to 8 bytes of it (two integers). Your program has the entire 4 kB allocated and you can write there without crashing. However, what happens if you call new
again? It is very likely that the memory allocator wrote some important tracking information somewhere into that 4 kB. The next bytes might be the size of the following block. Writing 54 into it might make it think it has more or less memory than it does. Or those bytes might be a pointer to the next block of free memory, and your 54 will cause the next memory allocation to crash the program.
You can write out of array range, but it is not guaranteed to work, and the data is not guaranteed to be persistent there, as something else can overwrite it.
It's simply not a good idea, and since there's no exception, potentially hard to find bug.
When reading that memory, you will be pulling some random garbage that was there left over from some other program or whatever used the memory before, so it can really be anything.
Accessing array out of range is undefined behavior. Thus 55 is one of many possible results and there is nothing surprising here.
C++ Standard n3337 § 5.7 Additive operators
5) When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integral expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i + n-th and i − n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.