September 1, 2021·4 min read·engineeringcstandardsallocationmemory

N2801 specifies what callers must know to free sized memory

Proposed sized deallocation functions let callers supply allocation size and alignment. The wording preserves null-pointer behavior while making incorrect size information undefined behavior.

Passing an allocation's size back to the allocator can save the allocator from recovering information the caller already knows. It also creates a new correctness obligation. The size supplied during release must describe the allocation being released, and an estimate of the object's currently useful contents is not necessarily that size.

David Goldblatt's N2801 wording paper develops that contract for C. The WG14 document log lists it on September 1, and the paper carries an August 30 date. It follows directional support for N2699 and supplies two wording candidates for free_sized and free_aligned_sized. These are proposed interfaces, not functions a portable C17 program can assume its library supplies.

I regard the contract as the main issue in this proposal. C already gives callers direct responsibility for allocation lifetime. Supplying size information can be useful, but only if the ownership representation retains the right size and carries it to the release point. The interface makes that information consequential.

Requested size is the portable input

The proposed free_sized takes a pointer and a size_t size. Its permitted allocation families are malloc, calloc, and realloc. In the second wording candidate, the size must equal the requested allocation size. The first candidate spells out the calloc case as the product of its element count and element size.

That wording distinguishes allocation extent from application-level length. A buffer can hold fewer live bytes than were requested at allocation. Passing the current content length would not satisfy the proposed contract unless it happens to equal the allocation request. An API designed around sized release therefore needs a way to retain allocation capacity separately where those values differ.

Reallocation adds a similar distinction. The wording refers to the request corresponding to the pointer returned by realloc. A retained size associated with the previous allocation is insufficient after a successful request of a different size. This follows from the parameter contract even when a particular allocator can resize an allocation without changing its address.

The proposal's ordinary sized function excludes results from aligned_alloc. Alignment-sensitive allocation has a separate release function with an additional parameter. That is a deliberate division between allocation families, so choosing the release function requires knowing how the allocation originated as well as knowing its byte count.

Aligned release has an additional requirement

free_aligned_sized takes the pointer, requested alignment, and requested size. Its matching allocation comes from aligned_alloc. The paper expressly excludes allocations from malloc, calloc, and realloc from this interface, just as it excludes aligned allocations from the ordinary sized form.

This division prevents callers from treating the new functions as arbitrary variants of free. The proposed equivalence to free applies when the stated preconditions hold. Outside those conditions, the behavior is undefined. Providing a correct pointer but an incorrect size or allocation-family history does not establish the required equivalence.

A conforming implementation may ignore the size and alignment information and call free. That permission is important for implementation scope. The new interfaces offer information to allocators that can use it, while allowing a library to implement the contract without a separate sized-release algorithm. The proposal does not promise that every implementation becomes faster.

The permission also cannot be used as a portable excuse for supplying inaccurate arguments. An implementation ignoring a parameter is one possible implementation of a function whose callers still have specified obligations. Another conforming allocator may rely on the information. Correct source must meet the interface contract across those choices.

Null remains a valid release argument

One requested revision fixes null-pointer behavior. Earlier wording began with a condition concerning pointers obtained from allocation functions. As Joseph Myers pointed out in reflector discussion, that unintentionally excluded null pointers that had not come from an allocator. N2801 explicitly includes a null pointer in each function's permitted inputs.

That repair preserves a useful property of ordinary release code. A cleanup path can hold null because no allocation was attempted, rather than because an allocation returned null. The validity of releasing that null value should not depend on reconstructing its history. The new wording makes this case explicit instead of relying on a reader to infer the intended analogy to free.

I find that revision more informative than a broad claim of compatibility. It identifies a specific way a familiar cleanup pattern would have become invalid under the earlier text. The corrected condition preserves that pattern while keeping the stricter requirements for non-null allocated pointers.

Usable-size extensions remain separate

The recommended practice discusses implementations that expose an allocation's usable size through an extension. It recommends allowing that reported usable size as an argument. This differs from the core portable requirement of supplying the requested size. An allocator may round a request internally, and the extension's result can describe that larger usable region.

Code relying on that extension needs the corresponding implementation guarantee. N2801's recommendation does not turn every allocator's internal rounded size into a portable input that a caller may guess. The distinction keeps the proposal's basic interface implementable while leaving room for allocators that already provide size-query facilities to integrate them consistently.