xanatos
xanatos

Reputation: 19

Sorting a list of students by name and grade using struct in C

I'm writing a program that sorts a list of students by name and grade. I'm receiving the following errors when I attempt to compile:

ex11.c: In function 'compareByGrade':
ex11.c:46: error: request for member 'grade' in something not a structure or union
ex11.c:47: error: request for member 'grade' in something not a structure or union
ex11.c: In function 'compareByName':
ex11.c:56: error: request for member 'name' in something not a structure or union
ex11.c:57: error: request for member 'name' in something not a structure or union

This is the header file:

#define CLASS_SIZE 10

struct student {
    char *name;
    int  idnumber;
    char grade;
};

This is the main file:

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

int main(void)
{
    int c, i;
    FILE *data;
    struct student tmp, class[CLASS_SIZE];
    void *nameArray[CLASS_SIZE], *gradeArray[CLASS_SIZE];
    static int compareByGrade(const void *first, const void *second);
    static int compareByName(const void *first, const void *second);

    data = fopen("data", "r");                         /* open data file */

/* Move data into struct */

    for (i = 0; i < CLASS_SIZE; ++i) {
            fscanf(data, "%s%d%c", tmp.name, tmp.idnumber, tmp.grade);
            class[i].name = tmp.name;
            class[i].idnumber = tmp.idnumber;
            class[i].grade = tmp.grade;
    }
    fclose(data);                                     /* close data file */
    qsort(nameArray, CLASS_SIZE, sizeof(*nameArray), &compareByName);
    qsort(gradeArray, CLASS_SIZE, sizeof(*gradeArray), &compareByGrade);
}

/* Function to sorty by grade */

static int compareByGrade(const void *first, const void *second)
{
    const char *firstGrade = (*(const char **) first) -> grade; /* line 46 */
    const char *secondGrade = (*(const char **) second) -> grade; /* line 47 */

    return strcmp(firstGrade, secondGrade);
}

/* Function to sort by name */

static int compareByName(const void *first, const void *second)
{
    const char *firstName = (*(const char **) first) -> name; /* line 56 */
    const char *secondName = (*(const char **) second) -> name; /* line 57 */

    return strcmp(firstName, secondName);
}

What am I doing wrong?

Upvotes: 0

Views: 4533

Answers (3)

Jonathan Leffler
Jonathan Leffler

Reputation: 754590

There are many things that you're doing wrong. These include:

  • Normally, you'd print out the results of sorting your array (or do something with them, if only to allow you to verify that the array is sorted).
  • You would normally write the comparison functions in terms of the structures — the routines would be passed a const struct student * disguised as a const void *.
  • You have a data structure containing a pointer but you don't allocate space for that pointer to point at.
  • You read into space that is not allocated.
  • You don't test that fopen() or fscanf() worked correctly.
  • Your scan format string doesn't allow for spaces between the ID number and the grade.

Here is working code based (loosely) on what you wrote.

Code

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

#define CLASS_SIZE 10

struct student
{
    char name[20];
    int  idnumber;
    char grade;
};

static int compareByGrade(const void *first, const void *second);
static int compareByName(const void *first, const void *second);
static void print_students(const char *tag, const struct student *class, size_t size);

int main(void)
{
    FILE *data;

    if ((data = fopen("data", "r")) == 0)
    {
        fprintf(stderr, "failed to open file data\n");
        exit(1);
    }

    struct student class[CLASS_SIZE];

    for (int i = 0; i < CLASS_SIZE; ++i)
    {
        char name[20];
        int  idnumber;
        char grade;
        if (fscanf(data, "%19s %d %c", name, &idnumber, &grade) != 3)
        {
            fprintf(stderr, "failed to read student %d\n", i);
            exit(1);
        }
        strcpy(class[i].name, name);
        class[i].idnumber = idnumber;
        class[i].grade = grade;
    }
    fclose(data);

    qsort(class, CLASS_SIZE, sizeof(class[0]), &compareByName);
    print_students("By name", class, CLASS_SIZE);
    qsort(class, CLASS_SIZE, sizeof(class[0]), &compareByGrade);
    print_students("By grade", class, CLASS_SIZE);
    return 0;
}

static int compareByGrade(const void *p1, const void *p2)
{
    const struct student *s1 = (const struct student *)p1;
    const struct student *s2 = (const struct student *)p2;

    if (s1->grade > s2->grade)
        return +1;
    else if (s1->grade < s2->grade)
        return -1;
    else
        return strcmp(s1->name, s2->name);
}

static int compareByName(const void *p1, const void *p2)
{
    const struct student *s1 = (const struct student *)p1;
    const struct student *s2 = (const struct student *)p2;

    return strcmp(s1->name, s2->name);
}

static void print_students(const char *tag, const struct student *class, size_t size)
{
    printf("%s (%zu students):\n", tag, size);
    for (size_t i = 0; i < size; i++)
        printf("%2zu: %-20s %4d %c\n", i, class[i].name, class[i].idnumber, class[i].grade);
}

Note how the compareByName() function determines first the grade rank and then compares by name.

Data set

student1 1234 A
student2 2345 B
junior03 3456 C
junior04 4356 D
sophomore12 5234 F
sophomore13 6345 B
senior19 3419 C
senior99 4399 D
fresher29 7456 C
fresher31 8356 A

Output

By name (10 students):
 0: fresher29            7456 C
 1: fresher31            8356 A
 2: junior03             3456 C
 3: junior04             4356 D
 4: senior19             3419 C
 5: senior99             4399 D
 6: sophomore12          5234 F
 7: sophomore13          6345 B
 8: student1             1234 A
 9: student2             2345 B
By grade (10 students):
 0: fresher31            8356 A
 1: student1             1234 A
 2: sophomore13          6345 B
 3: student2             2345 B
 4: fresher29            7456 C
 5: junior03             3456 C
 6: senior19             3419 C
 7: junior04             4356 D
 8: senior99             4399 D
 9: sophomore12          5234 F

Upvotes: 0

vvy
vvy

Reputation: 2073

Agreed with xaxxon. The program processes the struct student class, nameArray and gradeArray are never initialized.

BTW,I suppose not to use a struct named class, it's confused with a C++'s keyword.

grade is char, but strcmp() need string to compare.

Suggestion: In main(),

qsort(class, CLASS_SIZE, sizeof(struct student*), &compareByName);
qsort(class, CLASS_SIZE, sizeof(struct student*), &compareByGrade);

and outside,

int compareByGrade(const void *first, const void *second)
{
    const char firstGrade = ((struct student *)first) -> grade;
    const char secondGrade = ((struct student *)second) -> grade;
    return firstGrade<secondGrade?-1:(firstGrade==secondGrade?0:1);
}

/* Function to sort by name */

int compareByName(const void *first, const void *second)
{
    const char *firstName = ((struct student *)first) -> name;
    const char *secondName = ((struct student *)second) -> name;

    return strcmp(firstName, secondName);
}

the expression of return value of compareByGrade() would act in the same way of compareByName()

Upvotes: 1

xaxxon
xaxxon

Reputation: 19771

const char *firstGrade = (*(const char **) first) -> grade;
const char *secondGrade = (*(const char **) second) -> grade;

'first' is cast to a double pointer to char, which is then dereferenced, so you have a char *. You then dereference that with the -> to a char, and char isn't a struct or union and doesn't have a member grade (because it can't).

Same problem lower for name.

That said, I don't see where you are populating nameArray or gradeArray, so even if you fix the compilation errors, you're going to have numerous runtime errors.

Upvotes: 0

Related Questions