cdynamic-arrays

SIGBUS Error while accessing pointer in Implementation of Dynamic Arrays


It is just a partial implementation of dynamic arrays, all functions haven't yet been implemented and wanted to just check the working of a function.

This is the code

#include <stdio.h>
#include <stdlib.h>
#define MINCAP 100

typedef struct{
    void **buf;
    int size, capacity;
} DynArray;

void *peek_at_loc(DynArray *dyn, int loc){
    if (!dyn || loc < 0 || loc > dyn->size) return NULL;
    return dyn->buf[loc-1];
}

void initialise(DynArray *dyn, int capacity){
    if(!dyn || dyn->buf) return;
    if (capacity <= 0){
        dyn->capacity = MINCAP;
    }else{
        dyn->capacity = capacity;
    }
    dyn->buf = (void**)malloc(dyn->capacity*sizeof(void*));
}
int main()
{   
    DynArray control;
    int a, b;
    initialise(&control, 5);
    printf("%p", control.buf);
    *control.buf = &a;
    *(control.buf+1) = &b;
    control.size = 2;
    int *c = peek_at_loc(&control, 2);
    printf("%p %p", &b, c);
    free(control.buf);
}

I was getting SIGBUS error initially when I didn't free the pointer return to buf by malloc (I believe the memory should be de allocated after the process ends which would be fine). I have to been able to isolate the issue to accessing control.buf through the main function. I am not sure why this would be an issue, I am looking for an explanation and solution to this problem.

After adding the free command I get these errors.

Dynamic_Arr(27352,0x1da499ec0) malloc: *** error for object 0x10062bf30: pointer being freed was not allocated
Dynamic_Arr(27352,0x1da499ec0) malloc: *** set a breakpoint in malloc_error_break to debug 0x10062bf30zsh: abort      ./Dynamic_Arr

Solution

  • DynArray control;
    

    The problem is that an uninitialised structure is passed to initialize(), where it is checking:

    if(!dyn || dyn->buf) return;
    

    whether dyn->buf is a non-null pointer. Yet dyn->buf has an indeterminate value and was never initialized.

    The fix is to simply set all members to zero:

    // You can drop the 0 in C24
    DynArray control = {0};
    

    (I also see no reason for dyn->size and dyn->capacity to be signed types.)

    Apart from that, the value returned by malloc() need not be casted (it returns a generic void * that is implicitly converted to and from any other pointer type) and only serves to clutter one's code, at least for the past 35 years, or since the C89 standard.

    You also need to check whether malloc() actually succeeded. On failure, malloc() and family returns a null pointer. Failing to do so risks involving undefined behaviour by a subsequent null pointer dereference.

    There's also undefined behaviour in the call to printf() because the %p format specifier expects a void *. You need to cast the arguments to void *s.

    There's also a bug in peek_at_loc():

    void *peek_at_loc(DynArray *dyn, int loc){
        if (!dyn || loc < 0 || loc > dyn->size) return NULL;
        return dyn->buf[loc-1];
    }
    

    When loc is 0, and loc <= dyn->size, this would be trying to access dyn->buf[-1].

    Though again, I see no reason for a signed type here. loc should be a size_t, and you should be comparing against 0.