The gap between two consecutive allocations is not related to paging. Your allocations are so small that they reside in the data segment. Libc handles these internally - the space outside your sizeof int
bytes generally contains pointers to the previous and the next block of data and the size of allocation - after all free
will just get a pointer and it will need to figure out how much memory it is to deallocate.
Additionally both of these pointers are aligned to 16-byte boundary. C11 7.22.3 says that
The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated).
Thus even though you're using them for int
the C standard requires that the pointer returned be aligned for any data type - which on your implementation is 16 bytes.
If however you allocate an object that is very large, glibc will map entire pages using mmap
instead. Then the alignment (on my 64-bit computer) is exactly 16 bytes from the start of a 4K page:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *a = malloc(12345678);
int *b = malloc(12345678);
printf("\n a=%p \t b=%p \n",a,b);
}
when run
% ./a.out
a=0x7fb65e7b7010 b=0x7fb65dbf0010
One can see the mmap
calls with strace ./a.out
- there among other system calls there are
mmap(NULL, 12349440, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb65e7b7000
mmap(NULL, 12349440, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb65dbf0000
As for why the addresses keep changing from one execution to another - this is due to address space layout randomization, or ASLR - a security mechanism which makes it harder for evil crackers to predictably exploit undefined behaviour in your code.
P.S. If you really need to dynamically allocate space for 2 int
s at consecutive addresses, allocate an array.