0

I wrote a C program to practice DSA, specifically queues.

I wrote a function free_q() to free the dynamically allocated memories, but when I print the pointer to these structures, I get a memory address that is not equal to when I print %p NULL. I'd like to know why isn't the pointer Q set to NULL when freeing not only the dynam. all. structure but also the pointer to the array inside the structure.

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

struct queue_rec
{
    unsigned int front;
    unsigned int rear;
    unsigned int max_size;
    unsigned int size;
    int *arr_struct;
};

typedef struct queue_rec *QUEUE;


int 
isEmpty(QUEUE Q)
{
    return(Q->size==0);
}

int isFull(QUEUE Q)
{
    return(Q->size>=Q->max_size);
}

QUEUE
create_queue(unsigned int size)
{

    QUEUE Q = (QUEUE) malloc(sizeof(struct queue_rec));

    if(Q==NULL){printf("Out of space!");}
    else
    {
        Q->arr_struct = (int *) malloc(size * sizeof(int));

        if(Q->arr_struct == NULL){printf("Out of space!");}
        else
        {
            Q->size = 0;
            Q->front = 1;
            Q->rear = 0;
            Q->max_size = size;
        }
        
    }

    return Q;
    

}

unsigned int 
succ(unsigned int value, QUEUE Q)
{
    if(++value==Q->max_size){value=0;}
    return value;
}

void
enqueue(int data, QUEUE Q)
{
    if(isFull(Q)){printf("Queue is full!");}
    else
    {
        Q->size++;
        Q->rear = succ(Q->rear, Q);
        Q->arr_struct[Q->rear] = data;
    }
    
}

void 
dequeue(QUEUE Q)
{
    if(isEmpty(Q)){printf("Queue is empty!");}
    else
    {
        Q->size--;
        printf("%d", Q->arr_struct[Q->front]);
        Q->front = succ(Q->front,Q);
    }
    
}


void
free_q(QUEUE Q)
{
    free(Q->arr_struct);
    free(Q);
    Q = NULL;
}



int main()
{
    
    QUEUE Q = create_queue(4);
    free_q(Q);
    
    printf("%p\c", Q);
    printf("%p", NULL);

    


    return 0;
    
}

OUTPUT:

00031480
00000000
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
Dave Hlave
  • 45
  • 4
  • Take a look at [How do free and malloc work in C?](https://stackoverflow.com/questions/1957099/how-do-free-and-malloc-work-in-c) – lorenzozane Nov 18 '20 at 11:07
  • `Q = NULL;` Function parameters are passed by value in C. It means `Q` is a local variable in the function and setting it does not change the caller's variable. – kaylum Nov 18 '20 at 11:09
  • Does this answer your question? [Changing address contained by pointer using function](https://stackoverflow.com/questions/13431108/changing-address-contained-by-pointer-using-function) – kaylum Nov 18 '20 at 11:09
  • @kaylum gotchu, I set the free_q() function to take a pointer pointing to the Q pointer, so I passed &Q as a parameter, it's working now. Another confusion is, even if before I didn't change the caller's variable, I freed what it's pointing to, so why is it still pointing to some address even tho I freed it ? BECAUSE, when I assign a pointer to a dynamically allocated memory, and I'm out of space, the pointer points to NULL, so why when freeing it, it doesn't set the pointer to NULL?? – Dave Hlave Nov 18 '20 at 12:21
  • Use [valgrind](https://valgrind.org/) – Basile Starynkevitch Nov 18 '20 at 12:50

1 Answers1

0

free takes a pointer as argument, which means that it can only alter what the pointer is pointing to. It cannot change the pointer point to another object.

void foo(int *p) {
    // Code
}

int main(void) {
    int x = 42;
    int *p = &x;
    int *q = &x;
    foo(p);
    if(p == q) 
        puts("Equal address"); // Guaranteed to be printed
    if(*p == *q)
        puts("Equal value");   // Depends on body of foo()
}

The above code is guaranteed to print "Equal address" irregardless of the body in foo. But it's NOT guaranteed to print "Equal value". If the signature instead was void foo(int **p) and you called it with foo(&p) it would have been different.

If you really want, you could do like this:

void
free_q(QUEUE *Q)
{
    free((*Q)->arr_struct);
    free(*Q);
    *Q = NULL;
}

But I would in general not recommend such things. The need for nulling pointers can be a symptom that you don't have as much control as you think. When would you use it? To see if you have freed it properly? Not a good idea. Look at this:

QUEUE q = create_queue(42);
QUEUE p = q;
free_q(&q);
if(p == NULL) {
    // OOOPS

Instead, I'd do like this:

typedef struct queue_rec QUEUE; // No pointer

QUEUE
create_queue(unsigned int size)
{
    int *arr = malloc(size * sizeof *arr); 
    if(!arr){
        fprintf(stderr, "Out of space");
        exit(1);
    }
    return (QUEUE) { .max_size = size, .arr_struct = arr };
}

and if you for some reason want to dynamically allocate a whole queue, do it manually:

QUEUE *q = malloc(sizeof *q);
*q = create_queue(42);

Also, I would discourage typedefing pointers.

klutt
  • 30,332
  • 17
  • 55
  • 95