The declaration of free is this (using MinGW compiler in Code::Blocks, in stdlib.h): _CRTIMP void __cdecl __MINGW_NOTHROW free (void*);
You can see that free takes void pointer as it's parameter. When you'd try free(a+2), it doesn't work. When you add 2 to the pointer, you change the address of the pointer you are supposed to free & when you try to free memory from address that isn't allocated, there are unexpected results.
free() has a pointer for a parameter. When you call free with an argument like (a +2) the argument is evaluated, taking the address that a holds and offsetting it the equivalent of 2 types*. This new address is used in the construction of the parameter of free().
* the size of the offset is determined by the size of the type
A pointer is a memory address. There, I said it. The difference is nothing as far as C++ is concerned anyways. C++ thinks (myPointer) and (myPointer+2) are pointers. You can assign a pointer a raw value but the idea is to think what you assigned it was still a pointer.
myPointer = myPointer is valid because myPointer returns a pointer. myPointer = myPointer + 2 is valid because myPointer+2 returns a pointer.
but... myPointer = NULL? Well, it's valid but NULL is nothing but 0 (in C++ at least). Does that mean that you can assign a pointer a raw value but a pointer is still not the same as a raw value? In C++, it's designed to have no difference. See if you can find one. You can print it, you can add, subtract, what functionality does it have or not have compared to a raw value or "address"? The only difference in reality is that it is of pointer type. int* is not the same as int which is clear. However, (pInt + 2) will be evaluated to type int*.
This example ( http://codepad.org/En2eFbsu ) strangely compiles but it gives me a runtime error when calling free() which is normal. You said you got an error during printf so I'd like to ask what compiler are you using?
As you know, dereferencing a pointer reveals the address it's pointing to. When adding n to a non-dereferenced pointer, you're offsetting the address by n, changing the address the pointer is pointing to. When you do this during a call to std::free( ), you're telling it this:
My array starts at the address 0x0002, not 0x0000. My array holds 10 char types. Now I want you to delete this array.
So std::free( ) goes about its business and releases your resources. Little do you know, you've overstepped your arrays boundaries by 2 addresses.
How does it determine how much memory to free or even can it determine this? (sic)
Programmers don't need to concern themselves with the inner-workings of standard functions/classes. The only thing programmers need to know is how to use it efficiently and effectively.