Search code examples
cheap-memory

Annotating Heap-allocated Objects in C


When programming in C, distinguishing between pointers that point to stack memory and those that point to heap memory is of vital importance (e.g. one cannot call free with a reference to stack-allocated memory). Despite this, the C language itself provides no base facilities to qualify pointers to make the aforementioned distinction explicit.

Are there any programming patterns in which people use an empty macro definition called heap (i.e. #define heap) or something similar for use as a pointer qualifier?

The utiltiy of such a definition becomes obvious when looking at the type signatures of the functions in the following (admittedly contrived) example:

int *stack_add(int *a, const int *b)
{
    *a += *b;

    return a;
}

int *heap heap_add(const int *a, const int *b)
{
    int *heap sum = malloc(sizeof *sum);

    if (sum == NULL) {
        return NULL;
    }

    *sum = *a + *b;

    return sum;
}

Despite the usefulness of such an idiom, I cannot recall ever having seen this before.


Solution

  • There is no such construct in C because it would be completely useless. It would be non-typed and therefore non-enforceable. It would, therefore, be just as useful as comments, or "systems" hungarian notation. (https://en.wikipedia.org/wiki/Hungarian_notation)

    Even if such a construct was somehow typed, (a language keyword rather than a pre-processor macro, acting like a qualifier, like const,) it would still be useless, because it would be introducing two different kinds of pointers, that cannot be mixed, for no good reason.

    The problem with C is not the lack of some mechanism for distinguishing between heap pointers and non-heap pointers in the source code. The problem is lack of any runtime checking for what kind of pointer you are attempting to pass to free(), and the continued lack of any such runtime checking even after the invention of ASSERT(), combined with people's narrow-minded insistence on not using ASSERT() and the broader concept of debug-build-only runtime checks.

    You can add such a runtime checking facility yourself, by replacing the built-in malloc() and free() with your own, (on debug builds only,) which returns blocks of memory wrapped in slightly larger blocks of memory that contain special markers so that free() can do some sanity checking before mindlessly attempting to free anything that you pass to it.