In this post, we implement the First Fit Memory Allocation Algorithm in C++. When a new process (job) needs memory, First Fit scans the list of available memory blocks from the beginning and allocates the first block that is large enough to hold the job. It is the fastest of the three classic memory placement strategies β First Fit, Best Fit, and Worst Fit.
What is First Fit Memory Allocation?
Memory is divided into fixed-size blocks (also called partitions). Each block can hold at most one job at a time. When a job arrives:
- Scan memory blocks from block 1 onwards.
- Allocate the first free block whose size β₯ job size.
- Mark that block as occupied. Stop scanning (no need to look at remaining blocks).
- If no block fits, the job remains unallocated.
The leftover space inside an allocated block (block size β job size) is called internal fragmentation. Over time, First Fit tends to leave small, unusable gaps in the lower portion of memory (external fragmentation).
C++ Code Implementation
// ============================================================
// First Fit Memory Allocation Algorithm in C++
// Scans blocks from the start and allocates the FIRST one that fits
// ============================================================
#include <iostream>
using namespace std;
// Represents a single memory block (partition) in main memory
struct MemoryBlock {
int blockNo; // Block number (display order, 1-based)
int blockSize; // Total size of this memory block
int isOccupied; // 0 = free, 1 = allocated to a job
int jobNo; // Job number currently in this block (0 = empty)
};
// Represents a job (process) that needs memory
struct Job {
int jobNo; // Job number (1-based)
int jobSize; // Memory required by this job
int isAllocated; // 0 = not yet placed, 1 = successfully allocated
};
int main() {
// -------------------------------------------------------
// Step 1: Read memory block information
// -------------------------------------------------------
cout << "\n Enter No of Blocks in memory: ";
int blockCount;
cin >> blockCount;
MemoryBlock memBlocks[99];
for (int blockIdx = 0; blockIdx < blockCount; blockIdx++) {
cout << "\n Enter Size of Block " << blockIdx + 1 << ": ";
cin >> memBlocks[blockIdx].blockSize;
memBlocks[blockIdx].blockNo = blockIdx + 1;
memBlocks[blockIdx].isOccupied = 0; // All blocks start free
memBlocks[blockIdx].jobNo = 0; // No job assigned
}
// -------------------------------------------------------
// Step 2: Read job information
// -------------------------------------------------------
int jobCount;
cout << "\n Enter No of Jobs: ";
cin >> jobCount;
Job jobs[99];
for (int jobIdx = 0; jobIdx < jobCount; jobIdx++) {
cout << "\n Enter size of Job " << jobIdx + 1 << ": ";
cin >> jobs[jobIdx].jobSize;
jobs[jobIdx].jobNo = jobIdx + 1;
jobs[jobIdx].isAllocated = 0; // No block assigned yet
}
// Display entered job data
cout << "\n JobNotJobSize";
for (int jobIdx = 0; jobIdx < jobCount; jobIdx++) {
cout << "\n " << jobs[jobIdx].jobNo << "\t" << jobs[jobIdx].jobSize;
}
// -------------------------------------------------------
// Step 3: First Fit Allocation
// For each job, scan blocks from the beginning and allocate
// the FIRST free block that is large enough.
// The 'break' ensures we stop at the first fit (not the best fit).
// -------------------------------------------------------
for (int jobIdx = 0; jobIdx < jobCount; jobIdx++) {
for (int blockIdx = 0; blockIdx < blockCount; blockIdx++) {
// Allocate if: block is free AND block is large enough AND job not yet placed
if (memBlocks[blockIdx].isOccupied == 0
&& memBlocks[blockIdx].blockSize >= jobs[jobIdx].jobSize
&& jobs[jobIdx].isAllocated != 1) {
memBlocks[blockIdx].jobNo = jobs[jobIdx].jobNo; // Record which job
memBlocks[blockIdx].isOccupied = 1; // Mark block as taken
jobs[jobIdx].isAllocated = 1; // Mark job as placed
break; // First Fit: stop after finding the FIRST suitable block
}
}
}
// -------------------------------------------------------
// Step 4: Display allocation results
// -------------------------------------------------------
cout << "\n\n BlockNo\tBlockSize\tJobNo\tJobSize\tStatus\tInternal Frag";
for (int blockIdx = 0; blockIdx < blockCount; blockIdx++) {
cout << "\n " << memBlocks[blockIdx].blockNo
<< "\t\t" << memBlocks[blockIdx].blockSize;
if (memBlocks[blockIdx].jobNo != 0) {
int allocatedJobIdx = memBlocks[blockIdx].jobNo - 1; // Convert job number to array index
int internalFrag = memBlocks[blockIdx].blockSize - jobs[allocatedJobIdx].jobSize;
cout << "\t" << memBlocks[blockIdx].jobNo
<< "\t" << jobs[allocatedJobIdx].jobSize
<< "\t" << memBlocks[blockIdx].isOccupied
<< "\t" << internalFrag;
} else {
// Block is empty (no job allocated)
cout << "\t0\t0\t" << memBlocks[blockIdx].isOccupied << "\t0";
}
}
// Report any jobs that could not be allocated
cout << "\n";
for (int jobIdx = 0; jobIdx < jobCount; jobIdx++) {
if (jobs[jobIdx].isAllocated == 0) {
cout << "\n Job " << jobs[jobIdx].jobNo
<< " (size " << jobs[jobIdx].jobSize
<< ") could NOT be allocated β no suitable block found.";
}
}
cout << endl;
return 0;
}
Explanation of the Code
- Structures β
MemoryBlockholdsblockNo,blockSize,isOccupied(flag: 0=free, 1=taken), andjobNo(which job is inside).JobholdsjobNo,jobSize, andisAllocated(flag: 0=not yet placed, 1=placed). All names describe their purpose clearly. - First Fit loop β The outer loop iterates over all jobs. The inner loop scans blocks from index 0. The triple condition checks: block is free + block is big enough + job not already placed. The
breakstatement after allocation is critical β it exits the inner loop immediately, ensuring only the first suitable block is used (not the best one). - Internal Fragmentation β Computed inline as
blockSize - jobSize. Displayed per allocated block to show wasted memory. - Unallocated jobs β After all allocation attempts, any job with
isAllocated == 0had no suitable block and is reported.
Sample Output
Enter No of Blocks in memory: 4
Enter Size of Block 1: 30
Enter Size of Block 2: 15
Enter Size of Block 3: 50
Enter Size of Block 4: 20
Enter No of Jobs: 4
Enter size of Job 1: 10
Enter size of Job 2: 20
Enter size of Job 3: 30
Enter size of Job 4: 10
JobNo JobSize
1 10
2 20
3 30
4 10
BlockNo BlockSize JobNo JobSize Status Internal Frag
1 30 1 10 1 20
2 15 4 10 1 5
3 50 2 20 1 30
4 20 0 0 0 0
Job 3 (size 30) could NOT be allocated β no suitable block found.
Step-by-Step Explanation of Input/Output
Blocks in memory: Block1=30, Block2=15, Block3=50, Block4=20. Jobs: J1=10, J2=20, J3=30, J4=10.
- Job 1 (size 10): Scans from Block1. Block1 (size 30) is the first free block that fits β allocated. Internal frag = 30 β 10 = 20.
- Job 2 (size 20): Block1 is occupied. Block2 (size 15) is too small (15 < 20). Block3 (size 50) is the first fit β allocated. Internal frag = 50 β 20 = 30.
- Job 3 (size 30): Block1 occupied. Block2 (15) too small. Block3 occupied. Block4 (size 20) too small (20 < 30). No block fits β Job 3 unallocated.
- Job 4 (size 10): Block1 occupied. Block2 (size 15) is free and fits β allocated. Internal frag = 15 β 10 = 5.
Total internal fragmentation = 20 + 30 + 5 = 55. Notice that First Fit used the large Block3 (size 50) for small Job2 (size 20), leaving 30 units wasted and leaving no room for Job3. Compare with Best Fit, which successfully allocates all 4 jobs on the same input.
See Also
Bottom line isβ¦
First Fit is the fastest memory allocation strategy because it stops as soon as a fitting block is found. It is simple and performs reasonably well in practice. However, it tends to cluster allocations at the low end of memory, leaving large blocks intact at the high end while filling the beginning with partially-used blocks. Best Fit addresses internal fragmentation better, but at the cost of scanning all blocks. Modern operating systems avoid contiguous allocation entirely using paging and segmentation to eliminate external fragmentation.