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开发者_Go百科 *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) *~
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);
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.
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.
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.
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.
Objective-C does reference counting, freeing an object once its reference count hits zero. You could also look into smart pointers.
精彩评论