Need help using qsort with an array of structs - c

Now, I have seen various examples, but I don't get what they mean.
Here's my structure
typedef struct profile{
char gender[1];
double soc;
. . .
} PROFILE;
where soc is social security number that I'm going to be sorting by.
I know you need a compare function, but I don't know how to come up with the exact thing I need.

Here is an example of using qsort for an array of structs in C
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int price;
int id;
} order;
int compare(const void *a, const void *b) {
order *orderA = (order *)a;
order *orderB = (order *)b;
return (orderB->price - orderA->price);
}
int main() {
order list[6];
srand(time(NULL));
printf("Before sorting\n");
for (int i = 0; i < 6; i++) {
list[i].price = rand() % 10;
list[i].id = i;
printf("Order id = %d Price = %d\n", list[i].id, list[i].price);
}
qsort(list, 6, sizeof(order), compare);
printf("AFTER sorting\n");
for (int n = 0; n < 6; n++) {
printf("Order id = %d Price = %d\n", list[n].id, list[n].price);
}
return 0;
}
hope it helps
katerina dimitris
(all regards to pitsi)

Your Soc should almost certainly not be of type double, but anyway here's an example of what a compare function needs to return:
int compare(const void *p1, const void *p2)
{
const struct profile *elem1 = p1;
const struct profile *elem2 = p2;
if (elem1->soc < elem2->soc)
return -1;
else if (elem1->soc > elem2->soc)
return 1;
else
return 0;
}
Thanks for pointing out the const void *.
Here is a complete example (archived): Sorting Structures with the C qsort() Function

The strict version of a comparator takes two constant void pointers:
int compare(const void *v1, const void *v2)
{
const struct profile *p1 = v1;
const struct profile *p2 = v2;
if (p1->gender > p2->gender)
return(+1);
else if (p1->gender < p2->gender)
return(-1);
else if (p1->soc > p2->soc)
return(+1);
else if (p1->soc < p2->soc)
return(-1);
else
return(0);
}
This compares the gender field first, then the soc field. This is how you handle any multipart comparison.

To sort the array, use qsort() and pass a comparison function.
Here is one that produces the correct result for all possible values of the price member:
typedef struct profile {
char gender[1];
double soc;
int price;
...
} PROFILE;
int compare_price(const void *a, const void *b) {
const PROFILE *oa = a;
const PROFILE *ob = b;
return (oa->price > ob->price) - (oa->price < ob->price);
}
int compare_soc(const void *a, const void *b) {
const PROFILE *oa = a;
const PROFILE *ob = b;
return (oa->soc > ob->soc) - (oa->soc < ob->soc);
}
Notes:
the simple subtraction of values produces incorrect results if the difference does not fit in the int type. For example -2 and INT_MAX cannot be correctly compared with the subtraction method. It would not work for floating point values either.
the above method can be used for all comparable types, including double except for NaN.
If you wish to handle NaN, here is how to group them at the end:
#include <math.h>
int compare_soc_nan_at_the_end(const void *a, const void *b) {
const PROFILE *oa = a;
const PROFILE *ob = b;
if (isnan(oa->soc)) {
return isnan(ob->soc) ? 0 : 1;
} else
if (isnan(ob->soc)) {
return -1;
} else {
return (oa->soc > ob->soc) - (oa->soc < ob->soc);
}
}

Related

Adding new item to an array of structs in C

I have some structs as following:
typedef struct {
char debutAge[15];
char finAge[15];
} Age;
typedef struct {
char type[15];
char composants[50];
Age utilisation;
} Categorie;
typedef struct {
int code;
char nom[30];
float prix;
Categorie med;
int quantitie;
} Medicament;
#define MAX 100
typedef Medicament Pharmacie[MAX];
Pharmacie P;
int nb=0;
In my main function, I'm trying to add a new element to the Pharmacie P array, this is what I tried:
void main()
{
Medicament m = Saisir();
ajouterMedicament(P, nb, m);
afficherMedicaments();
}
int ajouterMedicament(Pharmacie *ph, int *nb, Medicament m) {
int i;
for (i = 0; i < *nb; i++) {
if (m.code == ph[i].code) {
ph[i].prix = m.prix;
ph[i].quantitie = m.quantitie;
}
return 1;
}
if (*nb < MAX) {
ph[*nb] = m;
*nb += 1;
return 1;
}
return 0;
}
But I'm getting an error on this line: mif (m.code == ph[i].code) { :
expression must have struct or union type
How can I solve this ?
You don't need to declare the first argument as Pharmacie *. Pharmacie is a typedef for an array, so you don't need to add *.
int ajouterMedicament(Pharmacie ph, int *nb, Medicament m) {
And in the call, you need to pass a pointer to nb so it can be updated:
ajouterMedicament(P, &nb, m);
In general it gets confusing when you use typedef for pointer types, and you ran into that. I recommend not doing that. See Is it a good idea to typedef pointers?

Generic implementation of InsertionSort using void pointers

I'm working for this ADT project and i need to implement the insertion sort algorithm and verify that it works fine in an appropriate test function, that apply the algorithm to an array of double, a string and a struct.
I'm using as a guideline this pseudocode:
procedure InsertionSort(a, n)
for i <- 1, (n-1) do
j <- 1
while (j>0) and (a[j] < a[j-1]) do
Swap(a, j-1, j)
end while
end for
end procedure
I can't understand what the problem is.
The test function gives me error on the first assert () [ in test_sort_algorithm(...) ], therefore telling me that the algorithm is not working properly. But I can't understand where the error is. I've tried to recreate the algorithm for a normal array, without using a void pointer, and everything works. So I guess my problem is that I didn't understand the use of void pointers.
Can anyone please help me understand what's wrong with my Insertion sort algorithm?
Thank you.
This is my attempt:
/**
* \brief Sorts the given array according to the insertion sort algorithm.
*
* \param base Pointer to the start of the input array.
* \param n Number of elements in the input array.
* \param size The size (in bytes) of each element of the array.
* \param cmp Pointer to the comparison function used to sort the array in
* ascending order.
* The comparison function is called with two arguments that point to the
* objects being compared and must return an interger less than, equal to, or
* greater than zero if the first argument is considered to be respectively
* less than, equal to, or greater than the second.
*/
void upo_insertion_sort(void *base, size_t n, size_t size, upo_sort_comparator_t cmp)
{
size_t i, j;
unsigned char *ptr = base;
for (i = 1; i <= n-1; i++)
{
j = i;
while ( (j > 0) && (cmp(ptr+j*size, ptr+(j-1)*size) < 0) )
{
swap(ptr+(j-1)*size, ptr+j*size, size);
j = j - 1;
}
}
}
void swap(void *a, void *b, size_t n)
{
void *tmp = malloc(n);
if (tmp == NULL) { abort(); }
memmove(tmp, a, n);
memmove(a, b, n);
memmove(b, tmp, n);
free(tmp);
}
upo_sort_comparator_t cmp is a pointer to a comparison function. Declaration:
/** \brief Type definition for comparison functions used to compare two elements */
typedef int (*upo_sort_comparator_t)(const void*, const void*);
As I say before this function must be tested, to see if the algorithm work properly.
Code:
#define N 9
struct item_s
{
long id;
char *name;
};
typedef struct item_s item_t;
static double da[] = {3.0,1.3,0.4,7.8,13.2,-1.1,6.0,-3.2,78};
static double expect_da[] = {-3.2,-1.1,0.4,1.3,3.0,6.0,7.8,13.2,78.0};
static const char *sa[] = {"The","quick","brown","fox","jumps","over","the","lazy","dog"};
static const char *expect_sa[] = {"The","brown","dog","fox","jumps","lazy","over","quick","the"};
static item_t ca[] = {{9,"john"},{8,"jane"},{7,"mary"},{6,"anthony"},{5,"stevie"},{4,"bob"},{3,"ann"},{2,"claire"},{1,"alice"}};
static item_t expect_ca[] = {{1,"alice"},{2,"claire"},{3,"ann"},{4,"bob"},{5,"stevie"},{6,"anthony"},{7,"mary"},{8,"jane"},{9,"john"}};
/* Comparators */
static int double_comparator(const void *a, const void *b);
static int string_comparator(const void *a, const void *b);
static int item_comparator(const void *a, const void *b);
/* Test cases */
void test_sort_algorithm(void (*sort)(void*,size_t,size_t,upo_sort_comparator_t));
static void test_insertion_sort();
int double_comparator(const void *a, const void *b)
{
const double *aa = a;
const double *bb = b;
return (*aa > *bb) - (*aa < *bb);
}
int string_comparator(const void *a, const void *b)
{
const char **aa = (const char**) a;
const char **bb = (const char**) b;
return strcmp(*aa, *bb);
}
int item_comparator(const void *a, const void *b)
{
const item_t *aa = a;
const item_t *bb = b;
return (aa->id > bb->id) - (aa->id < bb->id);
}
void test_sort_algorithm(void (*sort)(void*,size_t,size_t,upo_sort_comparator_t))
{
int ok = 1;
size_t i = 0;
double *da_clone = NULL;
char **sa_clone = NULL;
item_t *ca_clone = NULL;
ok = 1;
da_clone = malloc(N*sizeof(double));
assert( da_clone != NULL );
memcpy(da_clone, da, N*sizeof(double));
sort(da_clone, N, sizeof(double), double_comparator);
for (i = 0; i < N; ++i)
{
ok &= !double_comparator(&da_clone[i], &expect_da[i]);
}
free(da_clone);
assert( ok );
ok = 1;
sa_clone = malloc(N*sizeof(char*));
assert( sa_clone != NULL );
memcpy(sa_clone, sa, N*sizeof(char*));
sort(sa_clone, N, sizeof(char*), string_comparator);
for (i = 0; i < N; ++i)
{
ok &= !string_comparator(&sa_clone[i], &expect_sa[i]);
}
free(sa_clone);
assert( ok );
ok = 1;
ca_clone = malloc(N*sizeof(item_t));
assert( ca_clone != NULL );
memcpy(ca_clone, ca, N*sizeof(item_t));
sort(ca_clone, N, sizeof(item_t), item_comparator);
for (i = 0; i < N; ++i)
{
ok &= !item_comparator(&ca_clone[i], &expect_ca[i]);
}
free(ca_clone);
assert( ok );
}
void test_insertion_sort()
{
test_sort_algorithm(upo_insertion_sort);
}
int main()
{
printf("Test case 'insertion sort'... ");
fflush(stdout);
test_insertion_sort();
printf("OK\n");
return 0;
}

Pointers to Members equivalent in c

There is an array of structures that I want to sort by the value of specific fields. The data types in the fields are identical. In c++ I used pointer to member to avoid rewriting same sort for different fields.
#include<string.h>
typedef struct {
int id;
int year;
int price;
} example_struct;
void sort_arr(example_struct a[5], const char* usr_field) {
int example_struct::*field = nullptr;
if (strcmp(usr_field, "id") == 0)
field = &example_struct::id;
else if (strcmp(usr_field, "year") == 0)
field = &example_struct::year;
else if (strcmp(usr_field, "price") == 0)
field = &example_struct::price;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5 - i; j++) {
if (a[j].*field > a[j + 1].*field) {
buff = a[j];
a[j] = a[j+1];
a[j+1] = buff;
}
}
}
}
int main {
example_struct a[5];
fill_arr(a); //somehow filling the array
sort_arr(a,"year");
}
I want to know, whether this code can be somehow emulated on C and how to do this
You should be able to use offsetof macro to get the offset to the member you want to compare, then use pointer arithmetic to get the value of the member. Note you won't get any type information for that member, just the offset into the struct so you'll want to ensure you know the sizes of the member fields.
So to do your compares, you could do this:
void sort_arr(example_struct a[5], const char* usr_field) {
size_t offset;
if (strcmp(usr_field, "id") == 0)
offset = offsetof(example_struct, id);
else if (strcmp(usr_field, "year") == 0)
offset = offsetof(example_struct, year);
else if (strcmp(usr_field, "price") == 0)
offset = offsetof(example_struct, price);
...
if (*((int*)((char*)&a[j])+offset) > *((int*)((char*)&a[j+1])+offset)) {
...
It may help to define some macros to make the member access a little more pleasant to work with.
#define memberat(ref, offset, membertype) *((membertype*)((char*)ref)+(size_t)offset)
if (memberat(&a[j], offset, int) > memberat(&a[j+1], offset, int))
If you're dealing with members of different types, you'll have to use function pointers to handle the comparisons since how they are compared will differ.
The marco offsetof is the answer, but sorting this way is overly hard and not especially type-safe, (if one changes the type, one will probably not receive an error or maybe even a warning.) This is typical C code to sort; one has one function that picks out the int in the struct that one wants for each int value and compares for qsort.
#include <stdlib.h> /* EXIT_* qsort rand */
#include <stdio.h> /* printf */
#include <string.h> /* strcmp */
#include <assert.h> /* assert */
struct ExampleStruct { int id, year, price; };
static void fill(struct ExampleStruct *const a) {
assert(a);
/* <http://c-faq.com/lib/randrange.html> */
a->id = rand() / (RAND_MAX / 99998 + 1) + 1;
a->year = rand() / (RAND_MAX / 119 + 1) + 1900;
a->price = rand() / (RAND_MAX / 999999 + 1) + 1;
}
static void print(struct ExampleStruct *const a) {
assert(a);
printf("%05d\t%d\t$%d\n", a->id, a->year, a->price);
}
static void for_each(struct ExampleStruct *const a, const size_t a_size,
void (*const action)(struct ExampleStruct *const)) {
size_t i;
assert(a && action);
for(i = 0; i < a_size; i++) action(&a[i]);
}
static int cmp_int(const int a, const int b) { return (a > b) - (b > a); }
static int cmp_id(const void *const va, const void *const vb) {
const struct ExampleStruct *const a = va, *const b = vb;
return cmp_int(a->id, b->id);
}
static int cmp_year(const void *const va, const void *const vb) {
const struct ExampleStruct *const a = va, *const b = vb;
return cmp_int(a->year, b->year);
}
static int cmp_price(const void *const va, const void *const vb) {
const struct ExampleStruct *const a = va, *const b = vb;
return cmp_int(a->price, b->price);
}
int main(void) {
struct ExampleStruct a[5];
size_t a_size = sizeof a / sizeof *a;
for_each(a, a_size, &fill);
printf("Sorted by id.\n");
qsort(a, a_size, sizeof *a, &cmp_id);
for_each(a, a_size, &print);
printf("Sorted by year.\n");
qsort(a, a_size, sizeof *a, &cmp_year);
for_each(a, a_size, &print);
printf("Sorted by price.\n");
qsort(a, a_size, sizeof *a, &cmp_price);
for_each(a, a_size, &print);
return EXIT_SUCCESS;
}

calling a quick sort function with the comparison function as a parameter

This is probably a very basic question, but I'm having trouble with understanding pointers thoroughly. In my program below, in the main method, I'm just wondering what the right way to test my Qsort function is. Thanks in advance!
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
void swap(void *v[], int i, int j)
{
void *temp;
temp = v[i];
v[i] = v[j];
v[j]=temp;
}
int cmp1 (void *first_arg, void *second_arg)
{
int first = *(int *)first_arg;
int second = *(int *)second_arg;
if ( first < second )
{
return -1;
}
else if ( first == second )
{
return 0;
}
else
{
return 1;
}
}
int cmp2 (void * a, void * b)
{
return ( *(int *)a - *(int *)b );
}
void cmp3 (void *a, void *b)
{
char one = *(char *)a;
char two = *(char *)b;
if (one == two){
printf("The two values are equal");
}
else
{
printf("The two values are not equal");
}
}
void QSort(void *v[],int left, int right, int (*compare)(void *first, void *second))
{
int i, last;
void swap (void *v[],int ,int);
if(left >= right){
return;
}
swap(v,left,(left+right)/2);
last=left;
for(i=left+1;i<=right; i++){
if((*compare)(v[i],v[left])<0){
swap(v,++last,i);
}
}
swap(v,left,last);
QSort(v,left,last-1,compare);
QSort(v,last+1,right,compare);
}
int main()
{
int first = 23;
int second = 4;
int third = 5;
int temp[3];//={22,4,36,64,0};
temp[0] = (void *)&first;
temp[1]=(void *)&second;
temp[2]=(void *)&third;
QSort(temp, 0, 2, cmp1(.....));
for(int n=0;n<3;n++){
printf("%d ",*((int *)temp[n]));
}
return 0;
}
cmp1 is really the best way. It should always perform correctly.
cmp2 is close. It would work most of the time, but if you are dealing with very large integers, the results would be wrong.
cmp3 is definitely not right. The values are actually ints, but are being treated as chars. The results would be meaningless.
QSort(temp, 0, 2, cmp1(.....));
shoud be
QSort(temp, 0, 2, cmp1);
If foo is name of a function, then you use foo() to call it, and use foo to pass it as an argument to another function that requires a function pointer.
temp is not an integer array, it should be array of integer pointers.
line
int temp[3];
should be replaced with
int *temp[3];

Sorting an array of structures in C

Let's say I have structure like this:
typedef struct MyStruct{
char *string1;
int number1, number2, number3;
char string2[11], string3[9];
char *string4;
char *string5;
}MyStruct;
Programs prompts user to choose by what field it should sort the data. I am having trouble thinking of a way to sort array effectively. Do I really need to write separate sorting functions for each field? There must be some other way, because writing 8 functions, where 2 would suffice, doesn't look rational.
Look up qsort() from <stdlib.h>. It takes a comparator function. You can write separate comparator functions for the different sort orders, but still use the standard library qsort() to do the sorting.
For example:
int ms_cmp_string1(const void *vp1, const void *vp2)
{
const MyStruct *ms1 = vp1;
const MyStruct *ms2 = vp2;
int cmp = strcmp(ms1->string1, ms1->string2);
if (cmp != 0)
return cmp;
else if (ms1->number1 < ms2->number1)
return -1;
else if (ms1->number1 > ms2->number1)
return +1;
//...other comparisons as required...
else
return 0;
}
This is a decent outline for comparators. This one sorts on string1 and then by number1. You can either write variants that sort on different fields, or devise a scheme that applies the various possible tests in an order of your choosing. But the basic outline works pretty well and is suitable for passing to qsort() without any casts necessary.
You don't need to write 8 functions if only 2 are needed. Build your own qsort function and send a last parameter containing the member offset to the compare function, then, in your compare function, cast pointer + offset to the right type.
Something like:
int comp_int(const void *pa, const void *pb, size_t offset)
{
const int *a = (const int *)((const char *)pa + offset);
const int *b = (const int *)((const char *)pb + offset);
return *a - *b;
}
int comp_string(const void *pa, const void *pb, size_t offset)
{
const char *a = (const char *)pa + offset;
const char *b = (const char *)pb + offset;
return strcmp(a, b);
}
void swap(void *v[], int a, int b)
{
void *temp;
temp = v[a];
v[a] = v[b];
v[b] = temp;
}
void sort(void *v[], int left, int right, size_t offset, int (*comp)(const void *, const void *, size_t))
{
int i, last;
if (left >= right) return;
swap(v, left, (left + right) / 2);
last = left;
for (i = left + 1; i <= right; i++) {
if ((*comp)(v[i], v[left], offset) < 0)
swap(v, ++last, i);
}
swap(v, left, last);
sort(v, left, last - 1, offset, comp);
sort(v, last + 1, right, offset, comp);
}
offsetof can help
Here is a sample of using qsort from my another answer:
struct stringcase { char* string; void (*func)(void); };
void funcB1();
void funcAzA();
struct stringcase cases [] =
{ { "B1", funcB1 }
, { "AzA", funcAzA }
};
struct stringcase work_cases* = NULL;
int work_cases_cnt = 0;
// comparator function
int stringcase_cmp( const void *p1, const void *p2 )
{
return strcasecmp( ((struct stringcase*)p1)->string, ((struct stringcase*)p2)->string);
}
// prepare the data for searching
void prepare() {
// allocate the work_cases and copy cases values from it to work_cases
qsort( cases, i, sizeof( struct stringcase ), stringcase_cmp );
}
If you're using the GNU C library, there's an extension called qsort_r() that lets you pass an extra parameter to the comparison function.
Using some macros:
#include <stdio.h>
#include <stdlib.h>
struct data {
int x, y, z;
};
#define comp(member) comp_##member
#define comp_build(member) \
int comp_##member(const void *pa, const void *pb) \
{ \
const struct data *a = pa, *b = pb; \
return (a->member < b->member) ? -1 : (a->member > b->member); \
}
comp_build(x)
comp_build(y)
comp_build(z)
int main(void)
{
#define ROWS 3
struct data v[] = {
{3, 2, 1},
{1, 3, 2},
{2, 1, 3}
};
int i;
puts("Unsorted");
for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
qsort(v, ROWS, sizeof(struct data), comp(x));
puts("Sorted by x");
for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
puts("Sorted by y");
qsort(v, ROWS, sizeof(struct data), comp(y));
for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
puts("Sorted by z");
qsort(v, ROWS, sizeof(struct data), comp(z));
for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
return 0;
}

Resources