├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── Makefile ├── README.md └── src ├── alloc.c ├── alloc.h └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch", 6 | "type": "cppdbg", 7 | "program": "${workspaceRoot}/bin/main", 8 | "cwd": "${workspaceFolder}", 9 | "MIMode": "gdb", 10 | "miDebuggerPath": "/usr/bin/gdb", 11 | "preLaunchTask": "C/C++: gcc build active file", 12 | "request": "launch" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": [ 3 | { 4 | "type": "shell", 5 | "label": "C/C++: gcc build active file", 6 | "command": "make", 7 | "options": { 8 | "cwd": "${fileDirname}" 9 | }, 10 | "problemMatcher": [ 11 | "$gcc" 12 | ], 13 | "group": { 14 | "kind": "build", 15 | "isDefault": true 16 | }, 17 | "detail": "Task generated by Debugger." 18 | } 19 | ], 20 | "version": "2.0.0" 21 | } 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -g 3 | SRC_DIR=./src 4 | BIN_DIR=./bin 5 | OBJ_DIR=$(BIN_DIR)/objects 6 | 7 | main: $(SRC_DIR)/main.c $(OBJ_DIR)/alloc.o 8 | $(CC) $(CFLAGS) -o $(BIN_DIR)/main $(OBJ_DIR)/alloc.o $(SRC_DIR)/main.c 9 | 10 | $(OBJ_DIR)/alloc.o: $(SRC_DIR)/alloc.h $(SRC_DIR)/alloc.c 11 | mkdir -p $(BIN_DIR) $(OBJ_DIR) 12 | $(CC) $(CFLAGS) -o $(OBJ_DIR)/alloc.o -c $(SRC_DIR)/alloc.c 13 | 14 | .PHONY: clean 15 | clean: 16 | rm -rf bin 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Memory Allocator 2 | 3 | Oversimplified and inefficient memory allocator in C (only works on Linux). 4 | 5 | Compile the code: 6 | 7 | ```bash 8 | make 9 | ``` 10 | 11 | Run the example: 12 | 13 | ```bash 14 | ./bin/main 15 | ``` 16 | -------------------------------------------------------------------------------- /src/alloc.c: -------------------------------------------------------------------------------- 1 | #include "alloc.h" 2 | 3 | // Block header. Contains metadata about the allocated block. 4 | typedef struct Header { 5 | // Allocated size, excluding header size. 6 | size_t size; 7 | // Whether the block is currently free or not. 8 | unsigned is_free; 9 | // Next block. 10 | struct Header *next; 11 | } Header; 12 | 13 | // First allocated block. 14 | Header *first = NULL; 15 | 16 | // Last allocated block. 17 | Header *last = NULL; 18 | 19 | // Returns the machine word size alignment for the given value. 20 | static inline size_t align(size_t to_be_aligned) { 21 | return (to_be_aligned + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1); 22 | } 23 | 24 | // Returns the first block that can hold `size` amount of bytes and is free. 25 | Header *find_free_block(size_t size) { 26 | Header *current = first; 27 | 28 | while (current != NULL) { 29 | if (current->size <= size && current->is_free) { 30 | return current; 31 | } 32 | current = current->next; 33 | } 34 | 35 | return NULL; 36 | } 37 | 38 | void *alloc(size_t size) { 39 | // First try to find a free block that has been deallocated and has enough 40 | // capacity to store `size` bytes. 41 | Header *free_block = find_free_block(size); 42 | if (free_block != NULL) { 43 | // We are lucky and we don't have to do much work. 44 | free_block->is_free = 0; 45 | // The pointer where data can be written is located _after_ the header 46 | return ((void *) free_block) + sizeof(Header); 47 | } 48 | 49 | // If we didn't find any free blocks, we have to increment the program 50 | // break and allocate a new block. 51 | 52 | // The actual size that we need is the requested size + header size. 53 | // It should also be aligned for faster memory access. 54 | size_t total_size = align(sizeof(Header) + size); 55 | 56 | // sbrk is a syscall that can increment or decrement the _program break_. 57 | // It is obsolete and mmap is recommended instead, but sbrk is simpler. 58 | void *addr = sbrk(total_size); 59 | 60 | // Return NULL if sbrk fails 61 | if (addr == (void*) -1) { 62 | return NULL; 63 | } 64 | 65 | // Write the header contents at the beginning of the heap address 66 | Header *header = addr; 67 | header->size = size; 68 | header->is_free = 0; 69 | header->next = NULL; 70 | 71 | // Update linked list 72 | if (first == NULL) { 73 | first = header; 74 | last = header; 75 | } else { 76 | last->next = header; 77 | last = last->next; 78 | } 79 | 80 | // The pointer where data can be written is located _after_ the header 81 | return addr + sizeof(Header); 82 | } 83 | 84 | void dealloc(void *ptr) { 85 | // The actual address of the block is the given address - size of header 86 | // because when we allocate memory we return a pointer that points _after_ 87 | // the header. 88 | Header *header = ptr - sizeof(Header); 89 | 90 | // Mark the block as free to use. 91 | header->is_free = 1; 92 | 93 | // If the block is not the last block allocated, we can't really do much 94 | // because we cannot remove intermediate blocks, we can only decrement 95 | // the program break, so we can only release blocks in reverse order. 96 | if (header != last) { 97 | return; 98 | } 99 | 100 | // On the other hand, if we are so lucky that the block to be deallocated 101 | // is the last one, then we can release that memory back to the OS. So, 102 | // first search the previous block to update the tail in the linked list. 103 | if (first == last) { 104 | first = NULL; 105 | last = NULL; 106 | } else { 107 | Header *current = first; 108 | while (current->next != NULL && current->next != last) { 109 | current = current->next; 110 | } 111 | last = current; 112 | } 113 | 114 | // Now decrement program counter. 115 | sbrk(0 - header->size - sizeof(Header)); 116 | } 117 | -------------------------------------------------------------------------------- /src/alloc.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /** 4 | * Allocates `size` bytes and returns pointer to allocted memory or `NULL` on 5 | * failure. 6 | */ 7 | void *alloc(size_t size); 8 | 9 | /** 10 | * Deallocate previously allocated pointer. 11 | */ 12 | void dealloc(void *ptr); 13 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "alloc.h" 3 | #include 4 | 5 | // 16 Bytes total size 6 | struct Point { 7 | double x; 8 | double y; 9 | }; 10 | 11 | void fail(char *message) { 12 | fprintf(stderr, message); 13 | exit(1); 14 | } 15 | 16 | int main() { 17 | printf("Allocating memory block for struct Point { double x, y; }\n"); 18 | struct Point *block1 = alloc(sizeof(struct Point)); 19 | 20 | if (block1 == NULL) { 21 | fail("Failed allocating memory, got NULL pointer\n"); 22 | } 23 | 24 | printf("Address of allocated memory: %p\n", block1); 25 | printf("Requested size: %lu bytes\n", sizeof(struct Point)); 26 | 27 | printf("Writing content to allocated memory (x = 1.1, y = 2.2)\n"); 28 | block1->x = 1.1; 29 | block1->y = 2.2; 30 | 31 | printf("Reading written values: Point { x = %f, y = %f }\n", block1->x, block1->y); 32 | 33 | printf("\nAllocating memory block for 3 extra struct Point\n"); 34 | struct Point *block2 = alloc(sizeof(struct Point) * 3); 35 | 36 | if (block2 == NULL) { 37 | fail("Failed allocating memory, got NULL pointer\n"); 38 | } 39 | 40 | printf("Addresses:\n- Point 1: %p\n- Point 2: %p\n- Point 3: %p\n", block2, &block2[1], &block2[2]); 41 | printf("Requested size: %lu bytes\n", sizeof(struct Point) * 3); 42 | 43 | printf("\nDeallocating memory for first allocated block\n"); 44 | dealloc(block1); 45 | printf("Allocating again, should return the previously deallocated address\n"); 46 | block1 = alloc(sizeof(struct Point)); 47 | printf("Returned address: %p\n", block1); 48 | 49 | printf("\nDeallocating second block\n"); 50 | dealloc(block2); 51 | printf("Allocating another block, should have the same address as second block\n"); 52 | struct Point *block3 = alloc(sizeof(struct Point)); 53 | printf("Returned address: %p\n", block3); 54 | } 55 | --------------------------------------------------------------------------------