embedded_crysis embedded_crysis - 20 days ago 5
C Question

Why can't I call function pointer from returned struct in C?

Ok so I've only been doing C a few weeks and I have some problems. I have a priority queue filled with

Bsig
pointers. As an added feature, I want that whenever a
Bsig
is popped, we execute a custom function.

typedef struct bsig{
// some more fields here, but they're not important
struct onpop_t *on_pop;
} Bsig;

typedef struct onpop_t {
struct onpop_t *next;
int (*do_on_pop)(Bsig *caller);
} Onpop;

int pop(SigPQ *q, Bsig* ret_sig){
if (q->size < 1){
return 0;
}
Bsig* signal = q->signals[0];
assert(signal);
q->signals[0] = q->signals[q->size-1];
q->size--;
sigpq_heapify(q,0);

if(signal->on_pop){
signal->on_pop->do_on_pop(signal);
}
ret_sig = signal;
return 1;
}


So essentially, whenever we call pop, the do_on_pop function should be launched. Furthermore, pop takes a signal pointer which is overwritten by whatever is popped from the queue. All this happens in two files included by main.c. The following is from main (where
testpop
is a custom function that juts prints something random - it is declared and defined in the main.c file):

Bsig *sig1 = new_signal(1000, 0);
Onpop *onpop1 = malloc(sizeof(Onpop));
onpop1->do_on_pop = &testpop;
sig1->on_pop = onpop1;
push(pq, sig1);

Bsig *ret_sig;
pop(pq,ret_sig);


So far so good - the custom function
testpop
gets called. But

ret_sig->on_pop->do_on_pop(ret_sig);


from main gives a segmentation fault! I don't understand why. The ret_sig address and the signal address should be the same, and the function call is the same - the only difference is that one is called from main and one is called from the included .c file. Can anyone shed a light?

pat pat
Answer

You need to explicitly pass ret_sig by reference.

int pop(SigPQ *q, Bsig** ret_sig){
    if (q->size < 1){
        return 0;
    }
    Bsig* signal = q->signals[0];
    assert(signal);
    q->signals[0] = q->signals[q->size-1];
    q->size--;
    sigpq_heapify(q,0);

    if(signal->on_pop){
        signal->on_pop->do_on_pop(signal);
    }
    *ret_sig = signal;
    return 1;
}

And pass the address of ret_sig

Bsig *ret_sig;
pop(pq,&ret_sig);

Or change the function to return the pointer instead of an int (which is cleaner):

Bsig *pop(SigPQ *q){
    if (q->size < 1){
        return NULL;
    }
    Bsig* signal = q->signals[0];
    assert(signal);
    q->signals[0] = q->signals[q->size-1];
    q->size--;
    sigpq_heapify(q,0);

    if(signal->on_pop){
        signal->on_pop->do_on_pop(signal);
    }
    return signal;
}

And assign the return value:

Bsig *ret_sig = pop(pq);