Generic implementation of InsertionSort using void pointers - c

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;
}

Related

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;
}

How to correctly call this merge sort function?

I am trying to implement this merge sort function to sort an array of structs in c. When I call the function my program exits early, I think this is because my array i am sorting is of type row_t* and needs to be row_t**, I am unsure on how to correctly malloc my data in order to achieve this.
//I have copied relevant bits of my code below
//this is the struct i am trying to sort by the value S
typedef struct
{
double rho, u, v, x, y, flux_u, flux_v, S;
} row_t;
//This is where i allocate the array i want to sort
row_t* linear_row_arr = (row_t*)malloc(sizeof(row_t)*100);
//this is where i try to call the function,
//linear_row_arr is an array of row_t, with 100 elements
merge_sort((void**)linear_row_arr, 99, row_array_s_comp);
//This is the function i am trying to call.
void merge(void** array, int n, int mid, int cmp(const void*, const void*))
{
// (0) need extra space for merging
void** tmp = malloc(n * sizeof(void*));
void** left = array;
void** right = array + mid;
int i = 0;
int j = 0;
int left_size = mid;
int right_size = n - mid;
// (1) perform the merge
for (int k = 0; k < n; k++) {
if (j == right_size)
tmp[k] = left[i++];
else if (i == left_size)
tmp[k] = right[j++];
else if (cmp(left[i], right[j]) < 1)
tmp[k] = left[i++];
else
tmp[k] = right[j++];
}
// (2) copy the merged array
for (int i = 0; i < n; i++) {
array[i] = tmp[i];
}
// (3) clean up
free(tmp);
}
void merge_sort(void** array, int n, int cmp(const void*, const void*))
{
if (n > 1) {
int mid = n / 2;
merge_sort(array, mid, cmp);
merge_sort(array + mid, n - mid, cmp);
merge(array, n, mid, cmp);
}
}
int row_array_s_comp(const void* a, const void* b)
{
row_t* ra = (row_t*)a;
row_t* rb = (row_t*)b;
// with int data we can just subtract to get the right behaviour
return ra->S - rb->S;
}
When I run this the code exits early with no error message.
EDIT:
I tried using #Ian Abbott's solution and it produced a seg fault at my comparison function. Could it be that I used malloc instead of calloc to allocate the memory for my data?
// This is my function call
//100 elements of row_t*
merge_sort(linear_row_arr, 100, sizeof(row_t*), row_array_s_comp);
EDIT 2:
Thank you Ian, I have fixed my errors and now have a handy merge sort function at my disposal. I up voted your answer but it says it won be displayed publicly as i have less than 15 rep. If anyone needs it here is the final comparison function i used was
int row_array_s_comp(const void* a, const void* b)
{
row_t* ra = (row_t*)a;
row_t* rb = (row_t*)b;
// with double data we can just subtract to get the right behaviour
return (ra->S > rb->S) - (ra->S < ra->S);
}
and i called the function with
merge_sort(linear_row_arr, 100, sizeof(row_t), row_array_s_comp);
If anyone finds this useful feel free to upvote #Ians Abotts answer as it is correct but I can't.
Thanks again for your time!
Here is a simple implementation of a top-down merge sort of an array, using parameters similar to qsort. Time complexity is O(n log n). It uses temporary storage of similar size to the input array.
/* Subroutine to merge two input arrays into an output array. */
static void merge(void *out, const void *pa, size_t na,
const void *pb, size_t nb, size_t elemsize,
int (*cmp)(const void *, const void *))
{
while (na != 0 || nb != 0) {
if (na == 0 || nb != 0 && cmp(pa, pb) > 0) {
memcpy(out, pb, elemsize);
pb = (const char *)pb + elemsize;
nb--;
} else {
memcpy(out, pa, elemsize);
pa = (const char *)pa + elemsize;
na--;
}
out = (char *)out + elemsize;
}
}
/* Merge sort an array. */
void merge_sort(void *base, size_t nmemb, size_t elemsize,
int (*cmp)(const void *, const void *))
{
size_t nbottom;
size_t ntop;
void *midp;
void *bottom;
void *top;
if (nmemb <= 1) {
/* Too small to sort. */
return;
}
/* Sort the bottom half and the top half. */
nbottom = nmemb / 2;
ntop = nmemb - nbottom;
midp = (char *)base + (nbottom * elemsize);
merge_sort(base, nbottom, elemsize, cmp);
merge_sort(midp, ntop, elemsize, cmp);
/* Make temporary copies of the sorted bottom half and top half. */
bottom = malloc(nbottom * elemsize);
top = malloc(ntop * elemsize);
memcpy(bottom, base, nbottom * elemsize);
memcpy(top, midp, ntop * elemsize);
/* Do a sorted merge of the copies into the original. */
merge(base, bottom, nbottom, top, ntop, elemsize, cmp);
/* Free temporary copies. */
free(bottom);
free(top);
}

Pointers to pointers segmentation fault

I created an insertion sort method which accepts an array, its size and a comparator. The comparator function is this:
int compare_int_ptr(void* ptr1, void* ptr2) {
double i1 = *(double*)ptr1;
double i2 = *(double*)ptr2;
if(i1<i2) {
return -1;
}
if(i1 == i2) {
return 0;
}
return 1;
}
The insertion is this:
void insertion_sort(void** array, int size, CompFunction compare){
int i,j;
void* key;
for(i = 1; i<size;i++){
key = array[i];
for(j = i-1; j>=0 && compare(array[j],key)>=0;j--){
swap(&array[j+1],&array[j]);
}
array[j+1] = key;
}
}
If I try to execute it, I get the segmentation fault error, so I think that I didn't used the pointer correctly. Is it correct that when I do the swap i pass it with the &?
EDIT: this is where I call the method:
int main(int argc, char const *argv[]) {
if(argc < 2) {
printf("Usage: sortingfirstusage <file_name>\n");
exit(EXIT_FAILURE);
}
double* array = load_array(argv[1]);
insertion_sort((void**)array, 3, compare_int_ptr);
free(array);
return 0;
The array is correctly loaded because I printed all the elements before calling the insertion sort and they were in.
You are trying to sort an array of doubles. double *array points to the first of n elements:
array ==> [ double ] \
[ double ] |
. > n elements
. |
[ double ] /
You are casting array to a void **:
(void **)array ==> [ void * ] \
[ void * ] |
. > n elements
. |
[ void * ] /
It should not be hard to determine that trouble lies ahead. A void * is not a double. It may or may not be the same size as a double. It almost certainly doesn't point to a valid memory location, so if you dereference it you will invoke undefined behavior, almost certainly resulting in your program being killed by a signal. Unfortunately, your insertion_sort function does dereference it when it calls the comparison function:
key = array[i];
for(j = i-1; j>=0 && compare(array[j],key)>=0;j--){
array[i] and array[j] are both invalid void * values (because the underlying memory contains doubles, not void *s). Your comparison function dereferences them here:
double i1 = *(double*)ptr1;
double i2 = *(double*)ptr2;
ptr1 and ptr2 contain meaningless pointer values. They do not point to doubles. Dereferencing them invokes undefined behavior.
Here is a working version of insertion_sort using the same function type and equivalent functionality to the qsort function from the C standard library (although the function is a lot less efficient than qsort):
insertion_sort.h:
#ifndef INSERTION_SORT_H_INCLUDED__
#define INSERTION_SORT_H_INCLUDED__
#include <stddef.h>
void insertion_sort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
#endif
insertion_sort.c:
#include <string.h>
#include "insertion_sort.h"
void insertion_sort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *))
{
char (*b)[size] = base;
size_t i;
size_t j;
int cmp;
for (i = 1; i < nmemb; i++) {
j = i - 1;
/* search backwards for insertion point */
while ((cmp = compar(b + j, b + i)) > 0 && j > 0)
j--;
if (cmp <= 0)
j++; /* went back one too far */
if (j < i) {
/* rotate element i to position j, j to j+1, ..., i-1 to i */
char tmp[size];
memcpy(&tmp[0], &b[i][0], size);
memmove(&b[j + 1][0], &b[j][0], size * (i - j));
memcpy(&b[j][0], &tmp[0], size);
}
}
}
Here is an example of the usage of the above function:
main.c:
#include <stdio.h>
#include "insertion_sort.h"
int compar_double(const void *a, const void *b)
{
double d1 = *(const double *)a;
double d2 = *(const double *)b;
if (d1 < d2)
return -1;
if (d1 > d2)
return 1;
return 0;
}
void print_doubles(const double *d, size_t n)
{
size_t i;
for (i = 0; i < n; i++)
printf("%g\n", d[i]);
}
int main(void)
{
double numberlist[] = { 3.0, 1.0, 5.0, -4.0, 2.0 };
size_t len = sizeof numberlist / sizeof numberlist[0];
printf("Unsorted:\n");
print_doubles(numberlist, len);
printf("\n");
insertion_sort(numberlist, len, sizeof numberlist[0], compar_double);
printf("Sorted:\n");
print_doubles(numberlist, len);
return 0;
}
Here is the output produced by the above program:
Unsorted:
3
1
5
-4
2
Sorted:
-4
1
2
3
5
void qsort( void *ptr, size_t count, size_t size,
int (*comp)(const void *, const void *) );
Like qsort, your sorting function should take a void * as the first parameter, not void **. The double indirection is a mistake that you should have noticed when calling insertion_sort. Converting a double[] array to void ** requires a cast, whereas it can be converted automatically to void * without one.
Without knowing what load_array(argv[1]); does, the answer is impossible to predict as Peter suggested in the comments.
Assuming this function call is doing something legitimate and loading the array with pointers that can be de-referenced properly then your problem is the cast. You cannot make a pointer into a pointer to pointers. You have to pass the address of the pointer to accomplish what you want.
insertion_sort(&array, 3, compare_int_ptr);

C: Top-Down Merge Sort - why infinite recursion?

I'm trying to implement merge sort, where the original and auxiliary array are alternated for each recursion. It's based on a this Java code. The description reads as follows (Link):
Improvements. We can cut the running time of mergesort substantially with some carefully considered modifications to the implementation.
[...]
Eliminate the copy to the auxiliary array. It is possible to eliminate the time (but not the space) taken to copy to the auxiliary array used for merging. To do so, we use two invocations of the sort method, one that takes its input from the given array and puts the sorted output in the auxiliary array; the other takes its input from the auxiliary array and puts the sorted output in the given array. With this approach, in a bit of mindbending recursive trickery, we can arrange the recursive calls such that the computation switches the roles of the input array and the auxiliary array at each level.
The following C code is my attempt to alternate the roles of the two arrays:
#include <stdlib.h>
#include <string.h>
#include "mergesort.h"
#define THRESHOLD 20
static size_t size_m = 0;
static size_t elements = 0;
static size_t mod = 0;
static char *a = NULL;
static char *b = NULL;
static char *i = NULL;
static char *j = NULL;
static char *k = NULL;
static char *start = NULL;
static char *middle = NULL;
static char *end = NULL;
static char *e = NULL;
static int (*cmp_m)(const void *, const void *) = NULL;
void sort(char *a, char *b, size_t lmod, size_t rmod) {
elements = rmod-lmod+1;
//========== INSERTION SORT ==========
if(elements <= THRESHOLD) {
start = b+size_m*lmod;
end = b+size_m*rmod;
for(i = start; i <= end; i += size_m) {
memcpy(e, i, size_m);
for(j = i-size_m; j >= start && (*cmp_m)((void *)e, (void *)j) < 0; j -= size_m) {
memcpy(j+size_m, j, size_m);
}
memcpy(j+size_m, e, size_m);
}
return;
}
//========== SPLIT OPERATION ==========//
size_t mmod = (rmod-lmod)/2;
sort(b, a, lmod, mmod);
sort(b, a, mmod+1, rmod);
//========== CHECK IF CURRENT SUBARRAY IS ALREADY SORTED ==========//
if((*cmp_m)((void *)(a+size_m*mmod), (void *)(a+size_m*(mmod+1))) <= 0) {
memcpy(b+lmod, a+lmod, size_m*elements);
return;
}
//========== MERGE OPERATION ==========//
start = a+size_m*lmod;
middle = a+size_m*mmod;
end = a+size_m*rmod;
i = start;
j = middle+size_m;
for(k = start; k <= end; k += size_m) {
mod = k-a;
if(i <= middle && (j > end || (*cmp_m)((void *)i, (void *)j) <= 0)) {
memcpy(b+mod, i, size_m);
i += size_m;
} else {
memcpy(b+mod, j, size_m);
j += size_m;
}
}
}
void mergesort(void *array, size_t num, size_t size, int (*cmp)(const void *a, const void *b)) {
size_m = size;
threshold = THRESHOLD;
a = (char *)array;
b = (char *)malloc(num*size_m);
e = (char *)malloc(size_m);
cmp_m = cmp;
memcpy(b, a, size_m*num);
sort(b, a, 0, num-1);
free(b);
free(e);
}
After profiling with valgrind, it seems my code does infinite recursion (the message was "can't grow stack").
Why does my implementation do infinite recursion?
Perhaps, valgrind can't judge the element decreases or not by recursion.
Try the following code.
static void sort(char *a, char *b, size_t n) {
:
:
//========== SPLIT OPERATION ==========//
size_t m = n/2;
sort(b, a, m);
sort(b + m * size_m, a + m * size_m, n - m);

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