Using values from a struct in the compare function in qsort() - C99 - Dereferencing pointer to incomplete type

StackOverflow https://stackoverflow.com/questions/22854025

Вопрос

i am fairly new to c and struggling to properly use the C stdlib qsort() function.

This is relevant to education and as such i am only allowed to use C99 and standard libraries if this is important.

I have a list of items taken from a HashTable and put into a HashItem **array but then when sorting this i am struggling with the compare function, i cannot get the correct value out of the struct. I have looked around and seen a few solutions but they all seem to lead to a

[Error] dereferencing pointer to incomplete type

Here is the struct :

typedef struct {
char *word;
int occurences;
} HashItem;

And i am interested in comparing and sorting by the occurences value.

Here is the bit of code which calls the qsort:

int n = array->number_of_values;
HashItem **standard_array = array_getarray(array); 
qsort(standard_array, n, sizeof(HashItem*), compare_func);

Here is the compare function:

int compare_func(const void *a, const void *b){
const struct HashItem* aa = (HashItem*)a;
    const struct HashItem* bb = (HashItem*)b;
    int val_1 = aa->occurencies;
    int val_2 = bb->occurencies;

    if(val_1 == val_2){
    return 0;
    }else if(val_1 > val_2){
    return 1;
    }else{
    return -1;
    }
    }

Sorry for the formatting, i am new to asking questions here.

I hope you can help thankyou.

Array code :

/*DynArray is a dynamically resizing array that is used to hold values and retain size data throughout*/
typedef struct{
    int number_of_values; 
    int capacity;
    HashItem **items;
}DynArray;

/*Method to create a new dynamic array and return it */
DynArray* array_new(int file_size){
    DynArray *array = malloc(sizeof(DynArray));
    array->number_of_values = 0;
    array->capacity = file_size / 10;
    printf("capacity is %d " , array->capacity);
    array->items = malloc(sizeof(HashItem*)* array->capacity);
}

/*Method used to increase the size of the array and reallocate memory*/
void array_increase_if_full(DynArray *array){
    if (array->number_of_values >= array->capacity){
        array->capacity *= 1.25;
        array->items = realloc(array->items, sizeof(HashItem)*array->capacity);
    }
}

/*Method to add a string to the dynamic array specified */
void array_append(DynArray *array, HashItem *item){
    array_increase_if_full(array);
    array->items[array->number_of_values] = item;
    //printf("item %s added \n at position %d ", array->items[array->number_of_values]->word, array->number_of_values);
    array->number_of_values++;
}

/*Method used to get value at specified position for given array*/
HashItem *array_get(DynArray *array, int position){
    if(position >= array->number_of_values || position <0){
        printf("Index specified out of range");
        exit(1);
    }
    //printf("item %s at position %d retrieved", array->items[position]->word, position);
    return array->items[position];
}

HashItem **array_getarray(DynArray *array){
    HashItem **toreturn[array->number_of_values];
    int i;
    for(i = 0; i < array->number_of_values; i++){
        toreturn[i] = array_get(array, i);
    }
    return toreturn;
}

Printing the array from the main gives the correct unsorted values of word:occurences

Edit:

Thanks to everyone that took their time to help, it is now in a working state with Michaels suggestion, i no longer use the array_getarray() method and instead use:

int n = array->number_of_values;
int i;
HashItem **standard_array = malloc(n*sizeof(HashItem*));
for(i = 0; i < n; i++){
    standard_array[i] = array_get(array, i);
    printf("%s : %d \n" , standard_array[i]->word, standard_array[i]->occurences);
}
Это было полезно?

Решение

You structure declaration:

    typedef struct {
    char *word;
    int occurences;
    } HashItem;

declares a typedef name for an anonymous struct. There is a HashItem type that's a structure, but there is no struct HashItem type.

So when your compare_func() has the following declarations:

const struct HashItem* aa = (HashItem*)a;
const struct HashItem* bb = (HashItem*)b;

those struct HashItem* variables are pointers to a forward declared struct HashItem that has nothign to do with the HashItem strucuture above.

Just change those variable declarations to:

const HashItem* aa = (HashItem*)a;
const HashItem* bb = (HashItem*)b;

and/or change the declaration of the structure to:

    typedef struct HashItem {
    char *word;
    int occurences;
    } HashItem;

However, there's another issue (as mentioned in other answers): you are apparently sorting an array of pointers to HashItem objects, but your compare_function() is being written as if you're sorting an array of the objects (not pointers).

To address this:

int compare_func(const void *a, const void *b)
{
    // get HashItem*'s from the HashItem**'s
    const HashItem* aa = *((HashItem**)a);
    const HashItem* bb = *((HashItem**)b);

    int val_1 = aa->occurencies;
    int val_2 = bb->occurencies;

    if (val_1 == val_2) {
        return 0;
    } else if (val_1 > val_2) {
        return 1;
    } else {
        return -1;
    }
}

Finally (for now anyway), this function is returning the address to a local array, so the data it points to is no longer valid:

HashItem **array_getarray(DynArray *array){
    HashItem **toreturn[array->number_of_values];
    int i;
    for(i = 0; i < array->number_of_values; i++){
        toreturn[i] = array_get(array, i);
    }
    return toreturn;
}

I think you'll need to allocate the array you're retuning using malloc() or calloc() or something. But what I really think you need to do is step back and create some drawing of your data structures and think about the lifetime of the various objects contained in them and how those lifetimes can be tracked an managed so that you don't have leaks, double frees, or pointer dereferences to no longer valid objects.

Другие советы

Change qsort(standard_array, n, sizeof(HashItem), compare_func); to

qsort(standard_array, n, sizeof(HashItem*), compare_func);

In function void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));

the third parameter size_t size stands for:

Size in bytes of each element in the array.

It now looks to me like your problems are all springing from the first definition.

/*DynArray is a dynamically resizing array that is used to hold values and retain size data throughout*/
typedef struct{
    int number_of_values; 
    int capacity;
    HashItem **items;
}DynArray;

I see no reason for items to be a double-pointer. The comment says it should contain values, but a double-pointer pointing to an array would contain pointers, not the ultimate values. I think this initial misstep is causing you to trip everywhere else. Change it to

    ...
    HashItem *items;
    ...

and the rest should flow more naturally.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top