Block Splitting
Block Splitting: Reducing Internal Fragmentation
When you allocate 50 bytes but find a 1000-byte free block, the naive approach wastes 950 bytes. Block splitting divides large blocks to reduce this waste.
The Problem
Without splitting:
void *p1 = my_malloc(1000); // Get a 1000-byte block
my_free(p1); // Free it
void *p2 = my_malloc(50); // Reuse the 1000-byte block!
// 950 bytes wasted (internal fragmentation)
With splitting:
void *p1 = my_malloc(1000); // Get a 1000-byte block
my_free(p1); // Free it
void *p2 = my_malloc(50); // Split: use 50, remainder is new free block
void *p3 = my_malloc(100); // Use the remainder!
Memory Layout
Before split (1000-byte free block, requesting 50):
┌────────────────┬─────────────────────────────────────────┐
│ header (32B) │ data region (1000B) │
│ size=1000 │ │
│ free=1 │ │
└────────────────┴─────────────────────────────────────────┘
After split:
┌────────────────┬──────────┬────────────────┬─────────────┐
│ header (32B) │ data(56B)│ new header(32B)│ data(~900B) │
│ size=56 │ (USED) │ size=~900 │ (FREE) │
│ free=0 │ │ free=1 │ │
└────────────────┴──────────┴────────────────┴─────────────┘
↑
New block can satisfy future allocations!
Your Task
Implement block splitting in your malloc:
#define BLOCK_MAGIC 0xDEADBEEF
#define MIN_BLOCK_SIZE (sizeof(block_header_t) + 16)
// Check if block can be split for given size
int can_split(block_header_t *block, size_t size);
// Split block, returns new free block (or NULL if not split)
block_header_t *split_block(block_header_t *block, size_t size);
// malloc with splitting support
void *my_malloc(size_t size);
// free with magic validation
void my_free(void *ptr);
Critical Test: Remainder Must Be Usable
void *p1 = my_malloc(1000);
my_free(p1);
void *p2 = my_malloc(50); // Should split the 1000-byte block
void *p3 = my_malloc(100); // Should use the remainder!
// p2 and p3 should be DIFFERENT and VALID
// If p3 == NULL or p3 == p2, splitting failed
Alignment Matters
Always align sizes to 8 bytes before splitting:
size_t aligned = (size + 7) & ~7;
If you split without aligning, the new block's header might be misaligned, causing crashes on some architectures.
The Magic Field
Add a magic number to detect corruption:
typedef struct block_header {
size_t size;
int free;
unsigned int magic; // Set to BLOCK_MAGIC
struct block_header *next;
} block_header_t;
Validate magic before trusting any block data.
Run tests to see results
No issues detected