Implementing First Fit Algorithm in C++

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:

  1. Scan memory blocks from block 1 onwards.
  2. Allocate the first free block whose size β‰₯ job size.
  3. Mark that block as occupied. Stop scanning (no need to look at remaining blocks).
  4. 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

  1. Structures β€” MemoryBlock holds blockNo, blockSize, isOccupied (flag: 0=free, 1=taken), and jobNo (which job is inside). Job holds jobNo, jobSize, and isAllocated (flag: 0=not yet placed, 1=placed). All names describe their purpose clearly.
  2. 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 break statement after allocation is critical β€” it exits the inner loop immediately, ensuring only the first suitable block is used (not the best one).
  3. Internal Fragmentation β€” Computed inline as blockSize - jobSize. Displayed per allocated block to show wasted memory.
  4. Unallocated jobs β€” After all allocation attempts, any job with isAllocated == 0 had 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.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.