Aligned Memory Allocation

hard · memory, alignment, optimization

Aligned Memory Allocation

Return a payload with a requested power-of-two alignment while preserving the original allocation for the paired free. The supplied my_malloc is a limited, single-threaded teaching allocator with 8-byte rounding on the course's 64-bit Linux target. That policy is not the standard C allocation contract: standard allocation provides fundamental alignment, rather than one universal byte count.

Your interface

int is_power_of_two(size_t n);
size_t align_up(size_t n, size_t alignment);
void *my_aligned_alloc(size_t alignment, size_t size);
void my_aligned_free(void *ptr);
  • is_power_of_two returns 1 exactly for positive powers of two, otherwise 0.
  • align_up returns the least multiple of alignment greater than or equal to n. Return 0 for invalid alignment or an unrepresentable rounded result. align_up(0, 8) also legitimately returns 0. This helper does not allocate.
  • my_aligned_alloc rejects zero size and invalid alignment with NULL. Promote a valid alignment smaller than sizeof(void*) to sizeof(void*), so the saved original-pointer slot is suitably aligned on this target. The requested size need not be a multiple of alignment; this is a custom API.
  • On success, all size payload bytes must be writable, disjoint from the saved pointer, and preserved until the paired free. Separate live allocations cannot overlap. Allocation does not promise zeroed bytes.
  • Reject overflow in padding, pointer-slot addition, base-allocator rounding, header addition, or conversion to the signed sbrk increment. Rejected arithmetic makes no growth call and changes no existing block, list link, or live data. A growth failure also returns NULL without changing existing allocations.
  • my_aligned_free(NULL) does nothing. Every other input must be a live pointer returned by my_aligned_alloc; interior pointers, foreign pointers, and double frees are outside the contract. Recover and free its original allocation.

Derive the layout

Reserve size + (effective_alignment - 1) + sizeof(void*) bytes from the provided allocator, checking each addition before computing it. The first part of that allocation leaves room for the saved pointer. Round the address then save the original pointer immediately before the aligned payload. Advance within the acquired region; do not wrap an integer address while rounding.

raw allocation start                    aligned payload
| optional padding | saved raw pointer | size writable bytes | spare bytes |

For an invented raw address 1000, an eight-byte pointer, alignment 64, and size 20: begin the search at 1008, round to 1024, store the original pointer in [1016,1024), and return 1024. The caller owns [1024,1044); the paired free recovers 1000. The underlying header lies before the raw allocation, outside these reserved payload bytes.

Check the boundaries

For a power-of-two alignment, (n + alignment - 1) & ~(alignment - 1) works only when the addition is representable. Check n <= SIZE_MAX - (alignment - 1) first. With a toy maximum 255, rounding 250 to 8 would need 256 and must report failure rather than wrapping to zero and using a small allocation. A successful small allocation is not evidence that the original large request was satisfied.

Test sizes around an alignment boundary, promoted small alignments, invalid alignments, arithmetic rejection, writable payloads, and reuse after the paired free. The hidden tests use bounded simulated growth to check failure and unchanged ownership without trying to exhaust real memory.

Run tests to see results
No issues detected
    Join Discord