0

I would like to use qsort() to sort an array of the student struct. First have it sort by grade, and then try it again using the student ID.

I'm seeing two problems:

  1. Although the grades are sorted correctly (ascending order), why do the grades not correspond to the respective student name?
  2. Why does some of the student names get cut off (like 'Ver' instead of "Veronica"?

RESULTS

Sorted by grades 
Ver Grade: 28
Alistair Grade: 68
Fred Grade: 70
Erin Grade: 75
Lesli Grade: 78
Belind Grade: 81
Sash Grade: 84
Tom Grade: 87
Aretha Grade: 98
Candy Grade: 100

CODE

#include <iostream>
#include <string>
using namespace std;

struct student {
    int grade;
    int studentID;
    string name;
};

const int ARRAY_SIZE = 10;
student studentArray[ARRAY_SIZE] = {
    {87, 10001, "Fred"},
    {28, 10002, "Tom"},
    {100, 10003, "Alistair"},
    {78, 10004, "Sasha"},
    {84, 10005, "Erin"},
    {98, 10006, "Belinda"},
    {75, 10007, "Leslie"},
    {70, 10008, "Candy"},
    {81, 10009, "Aretha"},
    {68, 10010, "Veronica"}
};


// Comparator Function 
int compareFunc(const void* voidA, const void* voidB)
{
    student* studentA = (student*) voidA;
    student* studentB = (student*) voidB;
    return (studentA->grade - studentB->grade);
}

int main()
{
    // Use qsort to sort an array of our student struct 
    qsort(studentArray, ARRAY_SIZE, sizeof(student), compareFunc);
    
    // First sort by grade
    cout << "Sorted by grades \n";
    for (int i = 0; i < ARRAY_SIZE; i++)
    {
        cout << studentArray[i].name << " Grade: " << studentArray[i].grade  << '\n';
    }
    
    // Second sort by student ID

Not too sure where I went wrong, thoughts are appreciated.

wohlstad
  • 12,661
  • 10
  • 26
  • 39
  • 4
    You cannot use `qsort` to sort anything that is not *trivially copyable*. Because your struct contains a `std::string` it is not trivially copyable. I would do it the C++ way and use `std::sort` instead, `qsort` was designed for C (where everything is trivially copyable). – john Aug 22 '23 at 06:03
  • Code to use `std::sort` is `#include ` then `std::sort(student_array, student_array + ARRAY_SIZE, compareFunc);` – john Aug 22 '23 at 06:06
  • You will also need to change `compareFunc` like this `bool compareFunc(const student& a, const student& b) { return a.grade > b.grade; }` – john Aug 22 '23 at 06:11
  • The main reason why the behavior is undefined is that `qsort` could be optimized to do `memcpy`'s or similar functions to perform the swapping. Applying `memcpy` to non-POD types leads to undefined behavior. The `qsort` function knows nothing about non-POD types, since it is a `C` legacy function. -- *Not too sure where I went wrong* -- Simply, you tried to use `qsort` when it wasn't an option. – PaulMcKenzie Aug 22 '23 at 06:19
  • The use of `void*` and "C" style arrays seems to indicate you are more used to programming in "C". In C++ use std::array/[std::vector](https://en.cppreference.com/w/cpp/container/vector) for arrays and use [std::sort](https://en.cppreference.com/w/cpp/algorithm/sort) for sorting. Also stop using `using namespace std;` – Pepijn Kramer Aug 22 '23 at 06:24

1 Answers1

3

As @john commented above you cannot use std::qsort with a type which is not TriviallyCopyable (assuming C++11) or PODType (before C++11), and your isn't because it contains a std::string:

From std::qsort:

If the type of the elements of the array is not a PODType (until C++11) TriviallyCopyable type (since C++11), the behavior is undefined.

(emphasys is mine)

Instead you can use std::sort.
You can also use a lambda instead of an external compare function:

#include <algorithm>

// ...

std::sort(std::begin(studentArray), 
          std::end(studentArray),
          [](student const& a, student const& b) { return (a.grade > b.grade); });

Side notes:

  1. Why is "using namespace std;" considered bad practice?.
  2. In C++ it is recommended to use std::vector or std::array instead of raw C arrays.
wohlstad
  • 12,661
  • 10
  • 26
  • 39