How malloc works?
The question is kind of wrong.
In a typical OS, there exists the concepts of virtual memory and physical memory.
Physical memory exists typically in 4kb blocks, virtual memory likewise.
Each process has virtual memory - to each process the OS presents what appears to be the fully addressable memory range. So on a 32 bit machine, each process 'thinks' it has 4 GB of contiguous memory.
In reality, the OS, behind the scenes, is busy mapping virtual memory allocations onto real blocks of physical memory. So a, say, 400kb virtual memory allocation, is mapped onto 100 physical blocks. Those physical blocks do not need to be contigious (and almost never are - nothing stops it from happening, but on a machine doing any kind of work, it's highly improbable) but the virtual memory allocation does need to be contiguous.
So you can still run into virtual memory fragmentation. Here, a process requests a block of memory and there isn't, in that particular processes virtual memory map, a block of contiguous virtual memory such that the request can be satisfied.
That problem is the problem you're thinking of.
Standard malloc
is defined in the C standard to allocate a contiguous block of memory (at least it appears so to you) - it will return a null pointer if the allocation fails.
At a lower level, the OS will be doing something like what kotlinski or Blank Xavier have described in their respective answers.
From §7.20.3 of the ISO/IEC 9899-1999 C Standard:
The pointer returned if the allocation (by
calloc
,realloc
, ormalloc
) succeeds is suitably aligned so that it may be assigned to a pointer to any type of object and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated).
It is not that explicit, but the paragraph mentions 'accessing an array of such objects', and in the C standard, arrays are:
An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. (from §6.2.5)
Also note that subsequent calls to calloc
, realloc
, and malloc
do not guarantee contiguity or ordering of memory (with other memory blocks already allocated).
This point is also specified in §7.20.3.
The order and contiguity of storage allocated by successive calls to the
calloc
,malloc
, andrealloc
functions is unspecified.
The call to malloc
will either succeed in returning a logically contiguous block of memory from your program's HEAP memory space equal to the size requested or it will fail with a NULL pointer. "Logically contiguous" means that with a malloc
of this type:
int *ip; /* Nothing yet allocated (other than the size of a pointer... */
int ar[100]; /* 100 ints on the STACK */
ip = (int *)malloc(sizeof ar); /* if that succeeds, 100 ints on the HEAP */
will allocate space for 100 ints on your OS on the HEAP and return either NULL or the pointer. Separately, the array ar
is allocated on the STACK. Each array will be laid out with all the ints logically next to each other, at least as far your program knows. If they were not next to each other, you would not be able to address these blocks as arrays with the array[offset]
notation or with pointer arithmetic.
You can then access either STACK or HEAP blocks of memory with an array access or pointer access interchangeably like this:
ip[2]=22; /* the second element of ip[] is '22' */
*(ar+33)=3333; /* the 33 element of ar is '3333' */
i=*(ip+2); /* assign using pointers */
j=ar[33]; /* assign using array offsets */
If the memory block returned by malloc
were not logically contiguous to your program, you would not be able to access the block with pointer arithmetic or array subscripting.
Behind the scenes, your OS may move other blocks of memory that are moveable, use virtual memory, swap other items to virtual memory, etc, in order to increase the HEAP allocated to your program. Malloc can either be a very fast or very expensive call -- depending what else is going on on that system and the HEAP space allocated to your program.
HEAP memory (that part accessed with dynamic system calls in C) is potentially subject to fragmentation. Say you allocated the number of 20 byte blocks where memory becomes scarce. Now image that you free every other block of those blocks. You will have highly fragmented memory since blocks allocated with malloc
cannot be moved if it effects the pointer that the program uses to access the block. (It can be moved transparently, but don't count on that being efficient.)
If you are making many calls for HEAP memory, consider changing your logic to use realloc
to grow and shrink the memory as needed. A big 'gotcha' with realloc is the pointer to your existing data may change, so only use 1 pointer to it. Realloc allows the OS to move the data as needed to have a better fit with what is available on the HEAP. You will (mostly) avoid the potential for memory fragmentation that way.
For quick blocks of 20 bytes, consider using the STACK. That is what it is for. Look at this SO post to see characteristics of STACK vs HEAP.
Read the C Guide of calloc, malloc, realloc, free for more info.