简体   繁体   中英

What is the typical prototype for a deallocation function in C?

Looking at code on examples on StackOverflow I've noticed two distinct prototypes for object deallocation:

struct foo *foo_create(int);
void foo_free_v1(struct foo *);
void foo_free_v2(struct foo **);

void bar() {

    struct *foo = foo_create(7);

    // ...

    // Version 1
    foo_free_v1(foo);

    // Version 2
    foo_free_v2(&foo);
}

The standard library function free uses the first form.

Is the second form idiomatic in C? If yes, for what purpose?

The ones that take a pointer-to-pointer do so because they have the added convenience of automatically nulling out your variable for you.

They might look like:

void foo_free_v1(struct foo *f) {
    if (f == NULL) return; // This has been freed before, don't do it again!

    free(f->a);
    free(f->b);
    free(f->c);
    free(f);
}
void foo_free_v2(struct foo **f) {
    if (*f == NULL) return; // This has been freed before, don't do it again!

    free((*f)->a);
    free((*f)->b);
    free((*f)->c);
    free(*f);

    *f = NULL; // Null out the variable so it can't be freed again.
}

This attempts to protect against double-free bugs. How good of an idea that, is debatable. See the comment thread below.

In the first function

void foo_free_v1(struct foo *);

the original pointer used as an argument expression is passed to the function by value. It means that the function deals with a copy of the value of the pointer passed to the function.

Changing the copy does not influence on the original pointer used as an argument expression.

Consider for example the function definition

void foo_free_v1(struct foo *p)
{
    free( p );
    p = NULL;
}

This statement within the function

p = NULL;

does not change the original pointer to NULL . It sets to NULL the function local variable p . As a result the pointer used as an argument expression after calling the function will have an invalid value. That is a value that does not point to an existing object.

In the second function declared like that

void foo_free_v2(struct foo **);

the original pointer used as an argument expression is accepted by reference through a pointer to it. So dereferencing the pointer you have a direct access to the original pointer used as an argument expression.

Consider a possible function definition

void foo_free_v2(struct foo **p)
{
    free( *p );
    *p = NULL;
}

In this case in the statement

*p = NULL;

it is the original pointer that is set to NULL . As a result the original pointer does not have an invalid value.

Consider for example a sungly-linked list like

struct SinglyLinkedList
{
    int data;
     SinglyLinkedList *next;
};

In main you can declare the list like

struct SinglyLinkedList *head = NULL;

Then you can add new nodes to the list. As the pointer head is initialized then the function that adds new nodes will work correctly.

After that you can destroy the list.

If you will call the first function declared like

void foo_free_v1(struct SinglyLinkedList *head );

then after calling the function the pointer head declared in main will have an invalid value. That is there will be an inconsistency. The list does not have already elements but its pointer to the head ode is not equal to NULL .

Is you call the function declared lik

void foo_free_v1(struct SinglyLinkedList **head );

then indeed the pointer to the head node in main will be equal to NULL that indeed means that the list is empty. And if you have a function that checks whether a list is empty you can pass the pointer to the function without producing undefined behavior.

What is the typical prototype for a deallocation function in C?

Pass the pointer. Nothing is returned or rarely used if non- void .

void foo_free_v1(foo_type *);

Is the second form ( foo_free_v2(&foo); ) idiomatic in C?

No.

Classic foo_free(foo_type *) samples:

foo *f = malloc(sizeof *f);
...
free(f);

FILE *istream = fopen(...);         
int count = fscanf(istream, ...);
long offset = ftell(istream, ...);
fclose(istream);

When to use foo_free_v2(&foo); ?

Use foo_free_v2(&foo) when foo is not a pointer type.

Example:

typedef struct {
  int object1;
  int *object2;
  int *object3;
  ... 
} foo_type;

foo_type foo = { 0 }; 

foo_get_resources(&foo, ...);
foo_do_this(&foo, ...);
foo_do_that(&foo, ...);
foo_free(&foo);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM