-1

How would I deallocate a void double pointer?

deallocate(void** ptr)

This is the case I am trying to test against:

char* allocated = (char*)allocate_array(sizeof(char), BUFSIZ,0);

deallocate_array((void**)&allocated);

I need to check to see if the ptr is not NULL so here is my attempt:

if(ptr != NULL && *ptr != NULL){
      free(*ptr);
}
ptr = NULL;
return;
Mike Henke
  • 641
  • 2
  • 10
  • 24
  • There is need to check *ptr, [free can handle it being NULL](http://stackoverflow.com/questions/6084218/is-it-good-practice-to-free-a-null-pointer-in-c). – rettichschnidi Feb 12 '16 at 00:34
  • How was `ptr` allocated? Do `ptr` and `*ptr` both point to allocations? Are you trying to free both or just one allocation? – MooseBoys Feb 12 '16 at 00:35
  • `char* allocated = (char*)allocate_array(sizeof(char), BUFSIZ,0);` `deallocate_array((void**)&allocated);` is the case i'm supposed to test against – Mike Henke Feb 12 '16 at 00:37
  • 1
    You do not deallocate a pointer, you deallocate the object it points to. (In this case `ptr` is a pointer, `*ptr` is both a pointer and an object being deallocated, and `*(some_unknown_type*)*ptr` is an object being deallocated) – user253751 Feb 12 '16 at 00:41
  • Don't change your original question. When you edit, you can add additional information, but don't delete your original or all answers given before your edit will no longer make sense... – David C. Rankin Feb 12 '16 at 01:30
  • Your code violates the strict aliasing rule (`char *` is aliased as `void *`) – M.M Feb 12 '16 at 03:00
  • If you are using C++ it would be simpler to pass the ptr to deallocate by reference instead of by value. That way you don't have to dereference to change the value when you return. – bruceg Feb 12 '16 at 03:01
  • A "double pointer" is not a thing in C++. What you're playing with here is a "pointer to pointer". – mlp Jul 15 '19 at 16:27

2 Answers2

2

Your try is pretty good. You want to something like this:

void deallocate(void **ptr) {
    char *allocated = NULL;
    if (ptr) {
        allocated = *ptr;
    }
    if (allocated) {
        free(allocated);
        *ptr = NULL;
    }
}

You will call it like this:

deallocate(&allocated);

The reason you pass in the address of the pointer is so that you can set the pointer back to null after deallocation.

bruceg
  • 2,433
  • 1
  • 22
  • 29
0

You may be making this more complicated than need be. There is no reason to pass the address of (e.g. &allocated) to your deallocate function (except to set the value of the original pointer to NULL without utilizing a return). You can simply pass a pointer. Yes, the function will receive a copy of that pointer, but it will still contain the starting address of the block of memory to be freed. e.g.:

void deallocate (void *ptr)
{
    if (!ptr) return;
    free (ptr);
}

If your intent was to deallocate and set the value pointed to by the original pointer to NULL within your deallocate function, then yes, you do need to pass the address of the original so that setting the pointer to NULL is reflected back in the calling function (or you could make your function void * and return a pointer to the value set to null). Leaving the parameter passed to deallocate as a void pointer, you could do the following:

void deallocate_doubleptr (void *ptr)
{
    if (!ptr || !*(void **)ptr) return;
    free (*(void **)ptr);
    *(void **)ptr = NULL;
}

(note:, you do not pass a void ** parameter, you simply pass a void * (void) pointer and cast as required within the function. The parameter is still a void pointer.)

A short example illustrating the point that both are equivalent, except for the explicit assignment of NULL, and guessing at what your allocate_array would be:

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

void *allocate_array (size_t size, size_t nmemb, int set);
void deallocate (void *ptr);
void deallocate_doubleptr (void *ptr);

int main (int argc, char **argv) {

    size_t size  = argc > 1 ? (size_t)strtoul (argv[1], NULL, 10) : 1;
    size_t nmemb = argc > 2 ? (size_t)strtoul (argv[2], NULL, 10) : 128;
    int    set   = argc > 3 ? (int)strtol (argv[3], NULL, 10) : 0;
    char *str = "The quick brown fox jumps over a lazy dog.";

    char *allocated = NULL;

    if ((allocated = allocate_array (size, nmemb, set)))
        printf ("\nsuccessfully allocated '%zu' bytes initialized to '%d' ('%c').\n",
                size * nmemb, set, set);

    if (31 < set && set < 127) { /* if filled with printable ASCII */
        allocated[strlen (str)] = 0;                /* nul-terminate */
        printf ("allocated : '%s'\n", allocated);   /* output array */
    }

    strncpy (allocated, str, size * nmemb - 1);     /* copy str to array */
    allocated[size * nmemb - 1] = 0;                /* nul-terminate */

    printf ("allocated : '%s'\n", allocated);       /* output */

#ifdef DEALLOCDBL
    deallocate_doubleptr (&allocated);
    printf ("deallocated all memeory, pointer reinitialized to 'NULL'\n");
#else
    deallocate (allocated);
    printf ("deallocated all memeory.\n");
#endif

    return 0;
}

void *allocate_array (size_t size, size_t nmemb, int set)
{
    if (!size || !nmemb) {
        fprintf (stderr, "error: invalid size or number of members.\n");
        return NULL;
    }

    void *memptr = NULL;

    if (set != 0) {
        memptr = malloc (nmemb * size);
        memset (memptr, set, nmemb * size);
    }
    else
        memptr = calloc (nmemb, size);

    if (!memptr) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        exit (EXIT_FAILURE);
    }

    return memptr;
}

void deallocate (void *ptr)
{
    if (!ptr) return;
    free (ptr);
    ptr = NULL;
}

void deallocate_doubleptr (void *ptr)
{
    if (!ptr || !*(void **)ptr) return;
    free (*(void **)ptr);
    *(void **)ptr = NULL;
}

Compile

gcc -Wall -Wextra -o bin/allocate_deallocate allocate_deallocate.c

or

gcc -Wall -Wextra -DDEALLOCDBL -o bin/allocate_deallocate_dbl allocate_deallocate.c

(most compilers should take the same, or very similar options)

Output

The output is the same regardless of the implementation called. (e.g. allocate_deallocate or allocate_deallocate_dbl):

$ ./bin/allocate_deallocate 1 64 65

successfully allocated '64' bytes initialized to '65' ('A').
allocated : 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
allocated : 'The quick brown fox jumps over a lazy dog.'
deallocated all memeory.

Memory/Error Check

$ valgrind ./bin/allocate_deallocate 1 64 65

or

$ valgrind ./bin/allocate_deallocate_dbl 1 64 65

==13759== Memcheck, a memory error detector
==13759== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==13759== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==13759== Command: ./bin/allocate_deallocate
==13759==

successfully allocated '64' bytes initialized to '65' ('A').
allocated : 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
allocated : 'The quick brown fox jumps over a lazy dog.'
deallocated all memeory, pointer reinitialized to 'NULL'
==13759==
==13759== HEAP SUMMARY:
==13759==     in use at exit: 0 bytes in 0 blocks
==13759==   total heap usage: 1 allocs, 1 frees, 64 bytes allocated
==13759==
==13759== All heap blocks were freed -- no leaks are possible
==13759==
==13759== For counts of detected and suppressed errors, rerun with: -v
==13759== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

Look it over and let me know if you have any questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85