5

I use two variables in which memory is allocated dynamically, and I print the memory locations, but they are not consecutive. Why?

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int *a = malloc(sizeof(int));
    int *b = malloc(sizeof(int));
    printf("\n a=%p \t b=%p  \n",a,b);
}

The answers I get (in Linux) are

1st time:

 a=0x20a0010     b=0x20a0030

2nd time:

 a=0x657010      b=0x657030

3rd time:

 a=0x139e010     b=0x139e030 

Why is the exact difference between the memory locations of a and b variables the way it is in the 1st, 2nd and 3rd times?

Is this related to paging memory?

My processor is 64 bit.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
naveen kumar
  • 53
  • 1
  • 3
  • 2
    Does it matter? If you need consecutive addresses, you should be using an array. – r3mainer Jul 28 '17 at 03:49
  • While this is misguided, it is an interesting question. With nothing else, you might expect the memory locations to be consecutive – Justin Jul 28 '17 at 03:54
  • Your addresses are `32` bytes apart. If they were consecutive, you'd expect them to be `4` bytes apart (assuming that `sizeof(int) == 4`, which it seems to usually be) – Justin Jul 28 '17 at 03:56
  • 6
    `malloc()` allocates more than just the number of bytes requested, because it has to store extra tracking information in the memory block so `free()` knows how to release the memory correctly. Think about it. You are allocating `sizeof(int)` bytes. How do you think `free()` knows to release `sizeof(int)` bytes if you don't pass `sizeof(int)` as a parameter to it? That info has to be stored somewhere, and the only thing passed to `free()` is the memory pointer, so the info has to be in the memory block itself. – Remy Lebeau Jul 28 '17 at 03:57
  • 2
    With what @RemyLebeau said, it's also true that it can be more efficient to have `malloc()` only allocate blocks of a certain size or higher; if you request a smaller size, it can be promoted to this larger size – Justin Jul 28 '17 at 03:59
  • Thank you @ Justin .. you understood my question at proper.. thank you @remy lebeau ... your answer may be correct... You are telling that instead of allocating actually 4 bytes of memory for int ...it is allocating 32 bytes for tracking of some info to allow for free() and something else... that means it is using extra 24 bytes of memory for tracking .. that means for one integer it will take 32 bytes of memory ... Am I correct to your point... – naveen kumar Jul 28 '17 at 05:43
  • [Why are address are not consecutive when allocating single bytes?](https://stackoverflow.com/q/14616145/608639), [Why do successive calls to new\[\] not allocate contiguous memory?](https://stackoverflow.com/q/25677458/608639), etc. – jww Jul 28 '17 at 06:00
  • oh!!!! Thank you @jww – naveen kumar Jul 28 '17 at 09:00

3 Answers3

7

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 ints at consecutive addresses, allocate an array.

2

The operating system handles memory allocation, and there is no guarantee that this memory is contiguous when dynamically allocating two consecutive variables. I should also mention that this is the outcome of a defense mechanism known as ASLR. ASLR defends against buffer overflows by randomizing the location of a process during its execution, this may include the stack, heap, and libraries. This is why you notice these addresses changing. By the standard, you're only guaranteed the following.

ISO C11 7.22.3.4 Malloc

1) Synopsis

#include <stdlib.h>
void* malloc(size_t size);

2) Description The malloc function allocates space for an object whose size is specified by size and whose value is indeterminate.

3) Return The malloc function returns either a null pointer or a pointer to the allocated space.

Miket25
  • 1,895
  • 3
  • 15
  • 29
  • *"The operating system handles memory allocation"* - Not necessarily true. Once memory is acquired, the runtime may provide allocations if the memory was previously free'd. Also see [Will malloc implementations return free-ed memory back to the system?](https://stackoverflow.com/q/2215259/608639) – jww Jul 28 '17 at 06:03
0

As note at GNU Examples of malloc

Note that the memory located after the end of the block is likely to be in use for something else; perhaps a block already allocated by another call to malloc.

This actually means that, for each call to malloc the OS, depending on its memory management algorithm, finds the most appropriate / proper / suitable / efficient free space for the caller.

For example:

void* p_1 = malloc(4);
void* p_2 = malloc(4);

[oooo][xxxx][oooo][oooo]
^           ^
p_1         p_2
duong_dajgja
  • 4,196
  • 1
  • 38
  • 65