Search code examples
cconstants

const and destruction responsibility in C


Suppose I have a first struct type a_t and a second struct type b_t, and one of the fields of b_t is a pointer to an instance of a_t. I have creation and destruction functions:

b_t* b_t_create(a_t* a_t_instance) {
   b_t_instance = (b_t*) malloc(sizeof(b_t));
   b_t_instance.a_t_instance = a_t_instance;
   return b_t_instance;
}

void b_t_destruct(b_t* b_t_instance) {
   free(b_t_instance->a_t_instance);
   free(b_t_instance);
}

Thus destruction responsibility migrates from a_t to b_t... My question is; is it advisable to rewrite the first function with a const and de-const cast as follows?

b_t* b_t_create(const a_t* a_t_instance) {
   b_t_instance = (b_t*) malloc(sizeof(b_t));
   b_t_instance.a_t_instance = (a_t*) a_t_instance;
   return b_t_instance;
}

On first look, it seems reasonable because the function itself does not touch upon a_t_instance. On the other hand, it secretly gives it in the hands of something that will later on destruct it, and thus, in fact not so secretly, we have a de-const cast inside, because the struct field a_t_instance is of type a_t*, in view of that inevitable destruction...


Solution

  • In C idiom, when a functions receives a pointer to a dynamic object, it can borrow it, meaning that it will use it, but the caller will still be responsible for later destruction, or steal it and take ownership of the object. In that latter case, the caller is no longer responsible for later destruction. Borrowing in the more common use case, and stealing shall be documented.

    Here the b_t_create function steals the pointer. You normally use const to say that the object will not be changed (both to the compiler and to the user of the function). As the object is now intended to later be deleted at b_t destruction without any action on the object itself, you cannot say that is left unchanged. Hence you should not declare it const.