Disclaimer: Never, ever count on this working. Consider this only "toy code" and never use it in "real" software!
Often times, the new
operator ends up calling right to malloc()
, which is known to exhibit this behavior in many versions of libc.
The problem with your code is that your pointer is a char*
but the data you're after is probably really a size_t
(4 bytes on a 32-bit system).
The following code does demonstrate almost what you're after:
#include <stddef.h> // for size_t
#include <stdio.h>
void test(size_t size) {
size_t result;
char* p = new char[size];
result = *((size_t*)p - 1);
printf("Allocated: %d (0x%X) Preceding value: %d (0x%X)
",
size, size, result, result);
delete p;
}
int main() {
test(1);
test(40);
test(100);
test(0x100);
test(6666);
test(0xDEAD);
return 0;
}
Note that I'm first casting p
to a size_t*
, and then subtracting 1 (which equates to sizeof(size_t)
bytes).
Output:
$ ./a.exe
Allocated: 1 (0x1) Preceding value: 19 (0x13)
Allocated: 40 (0x28) Preceding value: 51 (0x33)
Allocated: 100 (0x64) Preceding value: 107 (0x6B)
Allocated: 256 (0x100) Preceding value: 267 (0x10B)
Allocated: 6666 (0x1A0A) Preceding value: 6675 (0x1A13)
Allocated: 57005 (0xDEAD) Preceding value: 57019 (0xDEBB)
So the output is close.
Looking at malloc/malloc.c
from glibc, we see the following comment:
Alignment: 2 * sizeof(size_t) (default)
(i.e., 8 byte alignment with 4byte size_t). This suffices for
nearly all current machines and C compilers. However, you can
define MALLOC_ALIGNMENT to be wider than this if necessary.
Minimum overhead per allocated chunk: 4 or 8 bytes
Each malloced chunk has a hidden word of overhead holding size
and status information
Minimum allocated size: 4-byte ptrs: 16 bytes (including 4 overhead)
8-byte ptrs: 24/32 bytes (including, 4/8 overhead)
These are excellent clues. There are two things that are probably happening:
- Your requested allocation sizes are being aligned up to the next alignment size.
- The lowest bits (not used because of the above alignment) are used for this "status information.
So we add the code to show numbers that "play along" with these rules:
#define SIZE sizeof(size_t)
#define MAX(x,y) ((x)>(y) ? (x) : (y))
#define align(x) (((x)+2*SIZE-1) & ~(2*SIZE-1))
#define mask(x) ((x) & ~0x3)
printf("align(size): 0x%X mask(result): 0x%X
",
align(MAX(size+SIZE, 16)), mask(result));
The size also includes SIZE
, and must be at least 16. This value is then aligned to the next 2*SIZE multiple. And the result we read out has the bottom 2 bits ANDed off. These are the "status information. The result:
$ ./a.exe
sizeof(size_t) = 4
size: 1 (0x1) result: 19 (0x13)
align(size): 0x10 mask(result): 0x10
size: 40 (0x28) result: 51 (0x33)
align(size): 0x30 mask(result): 0x30
size: 100 (0x64) result: 107 (0x6B)
align(size): 0x68 mask(result): 0x68
size: 256 (0x100) result: 267 (0x10B)
align(size): 0x108 mask(result): 0x108
size: 6666 (0x1A0A) result: 6675 (0x1A13)
align(size): 0x1A10 mask(result): 0x1A10
size: 57005 (0xDEAD) result: 57019 (0xDEBB)
align(size): 0xDEB8 mask(result): 0xDEB8
And there you have it!
Note that I'm using:
$ uname
CYGWIN_NT-6.1-WOW64
$ g++ --version
g++ (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)
Again, this is highly implementation-specific and should never be trusted. However, it is true that many allocators store the allocation size right before the actual block of memory.
See also: