1

Ubuntu 10.10 gcc 4.4.4

I am just experimenting with allocating and freeing.

However, I am trying to avoid the problem when a object is freed more than once.

However, when I test I notice that the obj that is created and freed doesn't return to a null state. So is there any condition that I can set that will avoid if this did happen?

I also tried setting the object to NULL after the free. However, it still tried to free the object.

This is a reference to this question, just so you know is not a duplicate: freeing allocated memory

My code below:

#include <stdio.h>
#include "objects.h"

int main(void)
{
    obj_t *obj = NULL;

    obj = create_object();

    destroy_object(obj);

    destroy_object(obj);

    return 0;
}

==

#ifndef OBJECTS_H_INCLUDED
#define OBJECTS_H_INCLUDED

typedef struct Obj_t obj_t;

obj_t* create_object();
void destroy_object(obj_t *obj);

#endif /* OBJECTS_H_INCLUDED */

==

#include <stdio.h>
#include <stdlib.h>

#include "objects.h"

struct Obj_t {
    int obj_id;
};

obj_t* create_object()
{
    obj_t *obj = malloc(sizeof obj);

    return obj;
}

void destroy_object(obj_t *obj)
{
    if(obj != NULL) {
        printf("Object [ %d ] will be deleted\n", obj->obj_id);
        free(obj);
    }
}

==

OBJECT_FILES = objects.o main.o
CFLAGS = -Wall -Wextra -Wunreachable-code -ggdb -O0
CC = gcc
TARGET = obj

$(TARGET): $(OBJECT_FILES)
    $(CC) $(CFLAGS) $(OBJECT_FILES) -o $(TARGET)

main.o: main.c objects.c
    $(CC) $(CFLAGS) -c main.c

objects.o: objects.c
    $(CC) $(CFLAGS) -c objects.c 

clean:
    rm -f $(OBJECT_FILES) $(TARGET) *~
Community
  • 1
  • 1
ant2009
  • 27,094
  • 154
  • 411
  • 609
  • If you set the pointer to `NULL` and try to `free` it again, it is a no-op. Where were you setting the pointer back to `NULL`? – wkl Nov 23 '10 at 03:49
  • Note that in C (but not C++), the declaration in the header of `obj_t *create_object();` says that `create_object()` is a function with an undefined - not empty - parameter list that returns an `obj_t` pointer. The compiler cannot ensure that you do not miscall it because you did not tell it what the parameter list should be. You should write `obj_t *create_object(void);` in the declaration, and for symmetry would then write `obj_t *create_object(void) { ... }` in the function definition. – Jonathan Leffler Nov 23 '10 at 04:13
  • @Jon, Thanks for the tip about the void parameter. – ant2009 Nov 23 '10 at 06:37

6 Answers6

8

The problem is that the value of the pointer in Main is never updated, because you're passing a copy of the pointer to destroy_object. What you need is a pointer to the pointer. Try this:

void destroy_object(obj_t **obj)
{
    if(*obj != NULL) {
        printf("Object [ %d ] will be deleted\n", *obj->obj_id);
        free(*obj);
        *obj = NULL;
    }
}

Then:

destroy_object(&obj);
Andrew Cooper
  • 32,176
  • 5
  • 81
  • 116
4

You cannot set the pointer to NULL inside your destroy_object function because the value of the pointer inside the function is a copy of the pointer.

In C and C++ how programmers avoid freeing or deleting an object twice is to not do it.

If you find yourself writing a program where you can't tell how many references an object has, the first thing to do is rethink your design.

Then if you really must, use a C++ shared_ptr, or implement your own reference count scheme in C. Both Perl and Python do it that way in their C implementations.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
  • @ant2009 you could use call-by-reference, this way you will work on the actual obj and not the copy. see: https://www.tutorialspoint.com/cprogramming/c_function_call_by_reference.htm – Gewure Mar 14 '17 at 13:51
2

As others have noted, setting obj to NULL inside destroy_object() is changing a local variable, and has no effect in main().

However, if you wish to have destroy_object() attempt to warn the programmer about double-frees, as a debugging aid, then you could use the concept of a poison value. In your example, you might reserve some particular invalid obj_id value to mean "already freed" - say, -1. Your destroy_object() function would then look like:

#define OBJ_POISON (-1)

void destroy_object(obj_t *obj)
{
    if (obj != NULL) {
        if (obj->obj_id == OBJ_POISON) {
            fprintf(stderr, "Double-free of object @ %p detected, aborting.\n", obj);
            abort();
        }

        printf("Object [ %d @ %p ] will be deleted\n", obj->obj_id, obj);
        obj->obj_id = OBJ_POISON; 
        free(obj);
    }
}

(Of course, for this to work your create_object() function must also set obj_id to a valid value, like 0).

Note that this isn't intended to allow double-freeing to "work fine" - it's just intended to make it more visible while debugging.

caf
  • 233,326
  • 40
  • 323
  • 462
  • 2
    Instead of the implementation-defined cast of -1 to a pointer to get the poison, you could instead use `static const obj_t poison;` and `#define OBJ_POISON (&poison)` or similar. Then the pointer is guaranteed to be unique. – R.. GitHub STOP HELPING ICE Nov 23 '10 at 05:48
  • It's actually a plain `int` in the allocated struct that I'm setting to -1. Obviously, reading freed memory is still a bug - but there's a *chance* that the poison value will remain there. – caf Nov 23 '10 at 09:24
1

This is not an entirely new idea, so you may want to read about Reference Counting and Smart Pointers.

The Standard Template Library (STL) and Boost both make heavy use of these concepts.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
1

Usually it's a good practice for an object to have only one owner that has the right to delete it. In the same time you can keep one single pointer to that object, and make sure that you set that pointer to NULL after you delete object. Or, there is always standard C++ solution: smart pointer.

ruslik
  • 14,714
  • 1
  • 39
  • 40
0

Objective-C does reference counting, freeing an object once its reference count hits zero. You could also look into smart pointers.

Community
  • 1
  • 1
Alex Reynolds
  • 95,983
  • 54
  • 240
  • 345