CodeRigor

FREE LESSON

malloc, calloc, realloc & ownership

Own C-allocated byte storage, check size products before allocation, preserve the old block on realloc failure and release with the matching allocator.

C allocation functions provide storage with explicit failure results. They do not run C++ constructors. At a C boundary, wrap the ownership and allocator pairing in a small class so early returns and moves cannot lose the block.

Growth preserves the prefix and clears the suffix
The wrapper initializes only new bytes after successful realloc; failure retains the entire old state.
Growth preserves the prefix and clears the suffix
The wrapper initializes only new bytes after successful realloc; failure retains the entire old state.

Know which family owns the block

malloc obtains uninitialized byte storage. calloc obtains storage with all bytes zero. free releases storage from the matching C allocation family. Do not combine malloc with delete or new[] with free. All-bits-zero is not a universal substitute for typed initialization, and raw C allocation does not construct a resource-owning C++ object.

Ordinary allocation alignment does not establish support for arbitrary over-aligned types. If an interface requires special alignment or construction, define those separately. The build deliberately owns only unsigned-char byte storage, avoiding a hidden generic-object container disguised as a byte buffer.

Follow one complete example

Run this program, then change one boundary while keeping the API preconditions valid. Predict its output before pressing Run.

C++
#include <cstdlib>
#include <cstdio>
#include <memory>
int main() {
    using block=std::unique_ptr<void,decltype(&std::free)>;
    block owner(std::calloc(8,1),std::free);
    if(!owner) return 1;
    auto bytes=static_cast<unsigned char*>(owner.get());
    bytes[3]=12;
    std::printf("%u %u\n",unsigned(bytes[0]),unsigned(bytes[3]));
}
Commit ownership after success
The temporary result protects the original owner on allocation failure.
Commit ownership after success
The temporary result protects the original owner on allocation failure.

Treat realloc as a transaction

For a nonzero request, failed realloc leaves the old block intact. Assign its result to a temporary and adopt the replacement only after success. Overwriting the sole owner first leaks the original block on failure. Success can relocate the block: discard every old borrowed pointer, even if the returned numeric address happens to match.

A growing block preserves the old prefix, but its new suffix is not initialized by realloc. Our wrapper explicitly zeroes that suffix. Shrinking preserves only the retained prefix. The wrapper handles zero size by releasing and resetting before realloc, rather than depending on zero-size behavior that differs by C edition and implementation.

Match storage with its release
The allocation family and object lifetime model travel with ownership.
Match storage with its release
The allocation family and object lifetime model travel with ownership.

Reject impossible sizes and test real failure paths

Before count times width, require count <= SIZE_MAX / width when width is nonzero. A wrapped product can allocate a tiny block for a huge logical array. An unchanged size needs no allocation call; a zero product has an explicit empty-state invariant.

Allocation failure tests should not try to exhaust the machine. Inject an equivalent realloc/free pair whose resize callback can deterministically fail while the old block remains live. Track the old pointer, size and sentinel bytes across that failure. Moves transfer both the allocation and its matching callbacks; move assignment first releases its previous allocation through its original allocator. A self-move must preserve the owner.

Read the return value

Predict this smaller case. The check item later changes the values, so explain the rule as well as the output.

C++
#include <cstdlib>
#include <cstdio>
int main(){auto p=static_cast<unsigned char*>(std::calloc(4,1));if(!p)return 1;p[2]=7;int total=0;for(int i=0;i<4;++i)total+=p[i];std::printf("%d\n",total);std::free(p);}

Complete the faded example

State the missing precondition or result check before filling the code. The fill exercise supplies the rest of the implementation.

C++
// Fill: reject count * width overflow before forming the product.
void* replacement=allocator_.resize(data_, next);
// Fill: preserve the old owner if replacement is null.
// Fill: adopt the pointer, clear the new suffix, then update size.

Check your model

Why must the allocator callbacks move together with the pointer instead of keeping the destination owner's previous allocator?

Practice the boundary

Complete the supplied move-only c_byte_buffer. It owns bytes through a matched c_allocator pair; defaults call realloc/free. The callbacks are valid nonthrowing C-allocation equivalents and remain usable throughout the owner's lifetime. Use them for every allocation/release. realloc(nullptr,n) provides the initial allocation.

resize_elements(count,width) returns false for multiplication overflow or allocation failure, leaving pointer, size and bytes intact. On success preserve the old prefix, zero-initialize every newly grown byte and set size to count*width. A zero product releases the block and restores nullptr/0 without calling realloc with size zero. An unchanged size succeeds without an allocator call. Free nonnull owned storage exactly once; move transfers its matching allocator, leaving the source nullptr/0. Self-move is harmless.

This owns raw byte storage, not constructed C++ objects or over-aligned storage. Callers must discard old borrowed pointers after a successful realloc, even if its numeric address happens to be unchanged.

Read → six checks → two bug hunts → fill → build. A successful graded submission is evidence; reading or watching alone does not grant mastery. Review cards reinforce API choices separately.

Watch and verify

The Watch & read panel links a free lecture for the relevant background and primary API references. The lecture notes say which parts are background; the tested contract here supplies the exact wrapper policy.

C standard-library course · C library review cards · Unix/Linux systems

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →