4

I'm trying to understand how deallocation of stack-allocated objects behaves. To be precise, I'm trying to find an explanation in the standard (N1570). Consider the following simple function:

void foo(){
    char test[4096];
    test[10] = 0;
}

Here the test array will be deallocated when foo exits. It is easy to see in objdump that test is allocated on the stack. The standard (emphasis mine) states:

An object whose identifier is declared with no linkage and without the storage-class specifier static has automatic storage duration, as do some compound literals.

So test has automatic storage duration. We can easily rewrite the function as follows:

void test(){
    char *test= malloc(4096 * sizeof(char));
    test[10] = 0;
    free(test);
}

But we have to deallocate it by ourselves, and yet test still has automatic storage duration.

QUESTION: How the standard specifies that char test[4096] will be deallocated on function exit? The standard does not state that test is allocated on the stack, it is implementation defined.

2
  • 1
    The standard doesn't specify that it gets deallocated, only that the memory turns invalid. Commented Nov 19, 2018 at 8:29
  • 1
    In the second case you're allocating a pointer to char not the memory of your array. Arrays and pointers aren't the same thing. So the only variable with automatic storage in your case is the pointer test, that will be deallocated at function exit. The memory allocated by malloc is taken from heap and is permanent memory (until not explicetly deallocated). Commented Nov 19, 2018 at 9:37

3 Answers 3

6

The standard describes the various storage durations at §6.2.4

1 An object has a storage duration that determines its lifetime. There are four storage durations: static, thread, automatic, and allocated. Allocated storage is described in 7.22.3.

2 The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.

6 For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. (Entering an enclosed block or calling a function suspends, but does not end, execution of the current block.) If the block is entered recursively, a new instance of the object is created each time. The initial value of the object is indeterminate. If an initialization is specified for the object, it is performed each time the declaration or compound literal is reached in the execution of the block; otherwise, the value becomes indeterminate each time the declaration is reached.

So you are pretty much correct. It doesn't at all describe when and how storage is deallocated per se. It only specifies when that storage is accessible with well-defined semantics. An implementation need not deallocate the storage of a variable with automatic storage duration right away, you just can't touch it if you want your program to be standard compliant.

For allocated storage the same goes, with the added caveat that you have to explicitly tell the implementation you are done with the storage. But even if you do "free" it, an implementation may hold on to it still for a while longer.

It's possible on paper for a very poor implementation to exist, one which never deallocates memory. But in practice, those are culled naturally because poor implementations of C just become disused by the masses, and abandoned.

Sign up to request clarification or add additional context in comments.

2 Comments

So it mean I was wrong about my second example. test has allocated storage duration there, right?
@St.Antario - Not test, but *test. The pointer has automatic storage duration. The pointee has allocated duration. It may seem like splitting hairs, but an important distinction.
1

This is about the lifetime of the allocated memory returned from the allocator function(s). For the memory allocator functions, it is stated explicitly that (Quoting from C11, chapter §7.22.3.1, emphasis mine)

[...] The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated). The lifetime of an allocated object extends from the allocation until the deallocation. [...]

So, you need to deallocate the allocated memory explicitly. Unless you deallocate explicitly, the memory allocated remains valid to be used (and subsequently cause a memory leak, if not handled properly).

OTOH, in the second snippet, the variable test is still goes out of scope on the function exit, as it has automatic storage. The memory allocated for the variable test (note: not the memory pointed to by test) is not valid anymore, and attempt to access that would be undefined behaviour. Remember, this is true for the variable test, i.e., after the function returns, &test becomes invalid, however, since the memory pointed by test was allocated through an allocator function, returning that pointer and using it from the function call would still be a valid access.

Quoting the spec again,

[...] If an object is referred to outside of its lifetime, the behavior is undefined. [...]

Comments

1

The test variable in your first example is an array of 4096 bytes.

The test in your second example is just a pointer variable (4/8 bytes) that you initialize with the pointer value obtained from malloc(3) from the heap space.

Both test variables (4096 and 4/8 bytes, resp.) are indeed deallocated automatically when your program leaves the routine, but you have allocated 4096 bytes extra in your second example, from the heap, of which C language has no idea (they were allocated in a library routine that has nothing special, and so they don't return automatically to the heap if you don't do that explicitly. ---the heap is a special memory place, known to malloc(), that allows you to get extra memory in any order, and returnable also in any order---)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.