4

I have an array structs that hold char names. I want to sort them alphabetically using qsort however I keep getting an error message saying "initialization discards ‘const’ qualifier from pointer target type". I believe my cmpmi() function and qsort arguments are correct. Any help is greatly appreciated!

My error is:

gcc -std=gnu11 -Werror -Wall -o main main.c -lm -g
main.c: In function ‘compmi’:
main.c:18:25: error: initialization discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
  const student_t **p1 = a;
                         ^
main.c:19:25: error: initialization discards ‘const’ qualifier from pointer target type [-Werror=discarded-qualifiers]
  const student_t **p2 = b;
                         ^
cc1: all warnings being treated as errors
makefile:2: recipe for target 'main' failed
make: *** [main] Error 1

This is my qsort function:

int compmi(const void *a, const void *b)
{
    const student_t **p1 = a;
    const student_t **p2 = b;
    return strcmp((*p1)->name, (*p2)->name);
}

Code:

int main(int argc, char **argv) {
    unsigned numrecords;
    int i = 0;
    int length_multiplier = 1;
    char *lettergrade;
    //char *input = NULL;
    //char *pItem;
    
    student_t **students = NULL;
    // OPENS THE FILE IN BINARY
    FILE *input_file;
    input_file = fopen("input.bin", "rb");
    // READS THE NUMBER OF RECORDS
    fread(&numrecords, sizeof(u_int32_t), 1, input_file);
    
    // LOOPING THROUGH EACH ENTRY
    for(i = 0; i <= numrecords; i++)
    {
        // ALLOCATES MEMORY
        students = realloc(students, sizeof(student_t *) * length_multiplier);
        students[i] = malloc(sizeof(student_t));
        
        students[i]->name = malloc(sizeof(student_t)* 20);
        fread(students[i]->name, sizeof(student_t), 20, input_file);//READ NAME
        fread(&students[i]->gpa, sizeof(student_t), 1, input_file); // READ GPA
        fread(&students[i]->age, sizeof(u_int32_t), 1, input_file);// READ AGE
        
        length_multiplier++;
        
    }
    //SORTING WITH QSORT
    qsort(*students, numrecords, sizeof(student_t *), compmi);

    // PRINTING OUTPUT
    for(i = 0; i < length_multiplier - 2 ; i++)
    {
        printf("%i of %d:\n", i + 1, numrecords);
        printf("Name: %s\n", students[i]->name);
        //printf("UPDATED GPA USE THIS: %.1f\n", students[i]->gpa);
        printf("GPA:  %.1f \n", students[i]->gpa);
        printf("Age:  %i\n", students[i]->age);
        printf("\n");
    }
    
    // FREEING MEMORY
    for(i = 0; i < length_multiplier; i++)
    {
        free(students[i]);
        
    }
    free(students);
    fclose(input_file);
    
    return 0;
}
  • Try replacing 'const student_t **' with ' const student_t * const *' – tstanisl Mar 06 '21 at 20:28
  • 2
    @collegecoder: please do not *fix* the code in the question as it makes comments and answers inconsistent with the question/ – chqrlie Mar 06 '21 at 20:56
  • `qsort(*students,...)` I think this should just be `students`, not `*students`. You want to sort the entire array of pointers, not the "array" which its first element points to. – Nate Eldredge Mar 06 '21 at 21:16
  • @rici - I think you are right. More coffee req'd. – David C. Rankin Mar 06 '21 at 21:16
  • 1
    `for(i = 0; i <= numrecords; i++)`: Looks like an off-by-one error, should be `i < numrecords`. – Nate Eldredge Mar 06 '21 at 21:17
  • And the arithmetic with `length_multiplier` looks really wrong. – Nate Eldredge Mar 06 '21 at 21:19
  • The second `malloc` allocates the wrong amount of space, and the fread calls are probably wrong. Try printing out each record after you read it to check the read worked successfully. (In fact it would be a good idea to have a separate function to do the reading of a single record) – M.M Mar 06 '21 at 22:35
  • I suspect memory leaks here. You have lots of `malloc`s in your code but almost no corresponding `free`s. – CiaPan Mar 08 '21 at 15:57

2 Answers2

2

The variable a points to a const qualified type, but p1 does not point to a const type (but what that points to is). You need to add const between the *s.

int compmi(const void *a, const void *b)
{
    const student_t * const *p1 = a;
    const student_t * const *p2 = b;
    return strcmp((*p1)->name, (*p2)->name);
}
too honest for this site
  • 12,050
  • 4
  • 30
  • 52
dbush
  • 205,898
  • 23
  • 218
  • 273
  • That fixes my error message but then when I try to print the students, they are just empty. What happens to the entries when they are returned? – collegecoder Mar 06 '21 at 20:45
  • @collegecoder Do as this comment says: https://stackoverflow.com/questions/66510293/initialization-discards-const-qualifier-from-pointer-target-type#comment117579381_66510293 to verify your data is correctly read _before_ qsorting instead of complaining they are incorrect _after _ some processing. – CiaPan Mar 08 '21 at 15:54
1

There are multiple problems in the code:

  • the comparison function converts pointers to constant objects to pointers to non constant objects themselves pointers to constant student objects. The original constness is not preserved. The definitions should be:

    const student_t * const *p1 = a;
    const student_t * const *p2 = b;
    
  • the reading loop should stop when i == numrecords so you should use i < numrecords instead of i <= numrecords.

  • the fread sizes are incorrect: you should specify the size of the member type, not that of the student_t structure.

  • you pass *students to qsort instead of the array pointer students.

Here is a modified version:

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

typedef struct student_t {
    char *name;
    float gpa;
    int age;
} student_t;

int compmi(const void *a, const void *b) {
    const student_t * const *p1 = a;
    const student_t * const *p2 = b;
    return strcmp((*p1)->name, (*p2)->name);
}

int main(int argc, char **argv) {
    unsigned numrecords;
    int i = 0;
    char *lettergrade;

    // OPENS THE FILE IN BINARY
    FILE *input_file = fopen("input.bin", "rb");
    if (input_file == NULL) {
        fprintf(stderr, "cannot open input.bin\n");
        return 1;
    }
    // READS THE NUMBER OF RECORDS
    fread(&numrecords, sizeof(unsigned), 1, input_file);
    
    // allocate the array directly, no need for realloc
    student_t **students = malloc(sizeof(*students) * num_records);
    if (students == NULL) {
        fprintf(stderr, "allocation error\n");
        return 1;
    }

    // LOOPING THROUGH EACH ENTRY
    for (i = 0; i < numrecords; i++) {
        // ALLOCATE MEMORY
        if ((students[i] = malloc(sizeof(student_t))) == NULL
        ||  (students[i]->name = calloc(21, 1)) == NULL) {
            fprintf(stderr, "allocation error\n");
            return 1;
        }
        // READ NAME, GPA and AGE
        if (fread(students[i]->name, 20, 1, input_file) != 1
        ||  fread(&students[i]->gpa, sizeof(students[i]->gpa), 1, input_file) != 1
        ||  fread(&students[i]->age, sizeof(students[i]->age), 1, input_file) != 1) {
            fprintf(stderr, "error reading data for record %d / %d\n", i + 1, numrecords);
            return 1;
        }
    }
    fclose(input_file);

    //SORTING WITH QSORT
    qsort(students, numrecords, sizeof(*students), compmi);

    // PRINTING OUTPUT
    for (i = 0; i < numrecords; i++) {
        printf("%i of %d:\n", i + 1, numrecords);
        printf("Name: %s\n", students[i]->name);
        //printf("UPDATED GPA USE THIS: %.1f\n", students[i]->gpa);
        printf("GPA:  %.1f\n", students[i]->gpa);
        printf("Age:  %i\n", students[i]->age);
        printf("\n");
    }
    
    // FREEING MEMORY
    for (i = 0; i < numrecords; i++) {
        free(students[i]->name);
        free(students[i]);
    }
    free(students);
    
    return 0;
}

Note however that using binary format for the data is problematic:

  • the sizes and representation of various types may change from one system to another.
  • binary files require a precise specification and are not easy to debug.
  • text files are a preferred format for interchange. They are easy to read and write.
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • You didn't make an allocation test complete: you test whether `students[i]->name != NULL` but you did not test `students[i]` for being not-null before assigning to `students[i]->name'. – CiaPan Mar 08 '21 at 17:34
  • @CiaPan: good point, answer amended. Given the `name` field seems to have a fixed length, it should be defined as an array `char name[21]`, not a `char *` pointing to allocated memory, but the OP did not provide the structure definition and might not be at liberty to change it. – chqrlie Mar 08 '21 at 20:00