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);
Related
i'm trying to make a qsort function from scratch that sorts an array of pointers to structs
this is the code i have right now
static void swap(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
void _qsort(void* list, int list_len, int left, int right,
int(*comp)(const struct shpg_item *a, const struct shpg_item *b)) {
void *vt, *v3;
int i, last, mid = (left + right) / 2;
if (left >= right)
return;
void* vl = (char*)(list + (left * list_len));
void* vr = (char*)(list + (mid * list_len));
swap(vl, vr);
last = left;
for (i = left + 1; i <= right; i++) {
// vl and vt will have the starting address
// of the elements which will be passed to
// comp function.
vt = (char*)(list + (i * list_len));
if ((*comp)(vl, vt) > 0) {
++last;
v3 = (char*)(list + (last * list_len));
swap(vt, v3);
}
}
v3 = (char*)(list + (last * list_len));
swap(vl, v3);
_qsort(list,list_len, left, last - 1, comp);
trace_int(1);
_qsort(list, list_len, last + 1, right, comp);
}
void list_sort(struct shpg_item **list, int list_len,
int(*comp)(const struct shpg_item *a, const struct shpg_item *b)) {
_qsort(*list,list_len,0,(list_len-1),comp);
}
but this gives a segmentation fault error , can any one tell me why and help me ?
void * pointer addition
void * pointer addition is undefined behavior. But since the usual UB is OK, this may or may not be OP's trouble.
void _qsort(void* list, int list_len, int left, ...
...
(list + (left * list_len)) // UB
Instead recommend casting before addition.
// void* vl = (char*)(list + (left * list_len));
void* vl = ((char*) list) + (left * list_len);
Other issues may exist
I haven't check the entire code but your swap function seems wrong. Depending on the comment lines in your code;
// vl and vt will have the starting address
// of the elements which will be passed to
// comp function.
if (list + (left * list_len)) and (list + (last * list_len)) are pointers to be swapped (pointers to a string or a struct, for example), your swap function decoration & your caller line should read as:
Swapping two integers, floats, doubles, etc (in general swapping values only):
void swap(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
}
...
int x = 5;
int y = 3;
swap(&x, &y);
If you need to swap two pointers (a char * string or another type of pointer pointing to a struct), you can just swap pointer values without swapping the content pointed in the actual memory:
void swap(void **a, void **b) {
void *t = *a;
*a = *b;
*b = t;
}
...
char *x = "some string";
char *y = "some other string";
swap(&x, &y);
I've included a working example in the middle part of this answer, and also added an example using qsort.
Taking a quick look at the code I see problem here:
void _qsort(void* list, ...
Since list is an array of pointers it should be:
void _qsort(void** list, ...
or
void _qsort(void* list[], ...
With this declaration, pointer arithmetic will not be an issue, for example, list+3 == &list[3] == pointer to the 3rd pointer in the array. There's no need to cast list, as void** list will work fine in the main part of the code. The only code that will do any casting is the caller's compare function.
You can choose to emulate qsort's compare function parameters using type void **: compare(list+i, list+j), but it would be simpler to use type void *: compare(list[i], list[j]).
Swap should use void** as parameters. The call would be
swap(list+i, list+j)
/* ... */
void swap(void **i, void **j){
void * t;
t = *i;
*i = *j;
*j = t;
}
There are some comments about a void pointer possibly having a different size than a struct pointer or any type of data pointer, and that this could cause an issue. If this was true, then the C library function qsort() would not work because the first parameter for qsort is a void pointer, which will result in the caller's pointer being cast to a void pointer. In the caller's compare function, both parameters are const void pointers which the caller's compare function has to cast to the actual pointer types. With qsort() and the caller's compare function, parameters are being cast both to and from void pointers without issue.
C guarantees that a void pointer can be used to hold any type of data pointer, so in essence a void pointer is a generic data pointer (in 16 bit segment or selector environments, a generic "near" data pointer).
This is a working example, using typical Lomuto partition scheme (pivot = a[hi]):
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int data;
char name[32];
}XMPL;
int cmpr(void * pi, void *pj)
{
if(((XMPL *)pi)->data < ((XMPL *)pj)->data)
return -1;
if(((XMPL *)pi)->data > ((XMPL *)pj)->data)
return 1;
return 0;
}
void swap(void **i, void **j){
void * t;
t = *i;
*i = *j;
*j = t;
}
void QuickSort(void **a, int lo, int hi, int(*cmpp)(void *, void *))
{
void *p;
int i, j;
while(lo < hi){
p = a[hi];
i = lo;
for(j = lo; j < hi; ++j){
if((cmpp(a[j], p) < 0)){
swap(a+i, a+j);
++i;
}
}
swap(a+i, a+hi);
if(i - lo <= hi - i){ /* avoid stack overflow */
QuickSort(a, lo, i-1, cmpp);
lo = i+1;
} else {
QuickSort(a, i+1, hi, cmpp);
hi = i-1;
}
}
}
#define COUNT (1024)
int main(int argc, char**argv)
{
XMPL *ax; /* array of structures */
XMPL **pax; /* array of pointers to structures */
int i;
ax = malloc(COUNT * sizeof(XMPL));
pax = malloc(COUNT * sizeof(void **));
for(i = 0; i < COUNT; i++){ /* init structs, array of ptrs */
ax[i].data = rand();
pax[i] = ax+i;
}
QuickSort(pax, 0, COUNT-1, cmpr);
for(i = 1; i < COUNT; i++){
if(pax[i-1]->data > pax[i]->data){
break;
}
}
if(i == COUNT)
printf("passed\n");
else
printf("failed\n");
free(pax);
free(ax);
return(0);
}
Hoare parition scheme will probably be a bit faster. However, in this case, merge sort should be faster than quick sort. Merge sort does more moves but fewer compares than quick sort, and in this case, only pointers are being moved, while the compare involves an indirection via a pointer and a call to a compare function via a pointer.
Same basic code, but using qsort. Note that the cmpr() function needed one more dereference for each parameter.
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int data;
char name[32];
}XMPL;
int cmpr(const void * pi, const void *pj)
{
if((*(XMPL **)pi)->data < (*(XMPL **)pj)->data)
return -1;
if((*(XMPL **)pi)->data > (*(XMPL **)pj)->data)
return 1;
return 0;
}
#define COUNT (1024)
int main(int argc, char**argv)
{
XMPL *ax; /* array of structures */
XMPL **pax; /* array of pointers to structures */
int i;
ax = malloc(COUNT * sizeof(XMPL));
pax = malloc(COUNT * sizeof(void **));
for(i = 0; i < COUNT; i++){ /* init structs, array of ptrs */
ax[i].data = rand();
pax[i] = ax+i;
}
qsort(pax, COUNT, sizeof(XMPL *), cmpr);
for(i = 1; i < COUNT; i++){
if(pax[i-1]->data > pax[i]->data){
break;
}
}
if(i == COUNT)
printf("passed\n");
else
printf("failed\n");
free(pax);
free(ax);
return(0);
}
I've been unable to find any question regarding this, and I think I'm going a bit crazy trying to figure this out.
I have the following code:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
int cmp_int(const void *a, const void *b)
{
return * (int *)a - * (int *)b;
}
int main(int argc, char *argv[])
{
int n = 10;
int **arr = calloc(n, sizeof(int *));
srand((unsigned int) time(NULL));
for (int i = n-1; i >= 0; i--) {
arr[i] = calloc(1, sizeof(int));
*(arr[i]) = rand() % 1000;
}
for (int i = 0; i < n; i++)
printf("%d ", *(arr[i]));
printf("\n");
qsort(arr, 10, sizeof(void *), cmp_int);
for (int i = 0; i < n; i++)
printf("%d ", *(arr[i]));
printf("\n");
free(arr);
return 0;
}
It's super basic, right? According to the manpage, the first argument is the pointer to the base element and the third argument is the size. However, I fail to get the array as a sorted result. I'm still really confused as to what the first and third argument to qsort should be since I suspect that that's where the fault is.
Any help is appreciated.
Thanks.
Edit: I should add that this code obviously does no error checking and that I was trying to test qsort with a double-pointer array of integers, so while yes I could use a regular array that was not the intended purpose of this code (it’s actually part of a bigger segment in a separate program).
Your program makes my head hurt. The reason you're not getting a correct sort is that the comparison function is wrong. It would need to be return **(int **)a - **(int **)b; to get a correct result.
However it's not worth fixing the problem that way. A list of at least some of the issues:
If you don't use argc and argv, don't declare them.
Cast in the srand call is unnecessary.
int comparison by subtraction is a bad idea because it can overflow.
calloc returns should always be checked for null (out of memory) results.
calloc isn't needed at all. Use a variable length array.
There's no need to allocate an array of pointers to ints. Just allocate an array of ints. Then your comparison works as-is.
The qsort call uses a hard constant 10 rather than n.
It's less error prone to give the element size by dereferencing the array name.
At the end you free the "spine" array but never the integer elements.
You should factor out a function to print the array.
Here's a version that addresses these.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int cmp_int(const void *va, const void *vb)
{
int a = *(int *)va, b = *(int *) vb;
return a < b ? -1 : a > b ? +1 : 0;
}
void print(int *a, int n) {
for (int i = 0; i < n; ++i) printf("%d ", a[i]);
printf("\n");
}
int main(void)
{
int n = 10, a[n];
srand(time(0));
for (int i = 0; i < n; ++i) a[i] = rand() % 1000;
print(a, n);
qsort(a, n, sizeof a[0], cmp_int);
print(a, n);
return 0;
}
The problem you are having is failing to account for one additional level of indirection created by allocating for a block of pointers with int **arr = calloc (n, sizeof *arr); and then allocating storage for a single int to each pointer with arr[i] = calloc (1, sizeof *arr[i]).
Since the int compare (const void *a, const void *b) compare function for qsort expects a pointer to the elements of the array being sorted, both a and b above will be pointer-to-pointer to int in your case requiring 2 levels of indirection be dereferenced before the integer values can be compared.
Rather than cmp_int, you actually need a cmp_int_ptr compare function. It can be written as:
int cmp_int_ptr (const void *a, const void *b)
{
int *ai = *(int * const *)a,
*bi = *(int * const *)b;
return (*ai > *bi) - (*ai < *bi);
}
(note: the two levels of indirection in the cast (int * const *)... which can also be written as (int **), but to correspond to the parameter type (const void *) the (int * const *) is proper)
Putting that in place, adding validations for each allocation and cleaning up your calloc type-size specification by using the dereferenced pointer itself to set type-size, you can do:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
int cmp_int_ptr (const void *a, const void *b)
{
int *ai = *(int * const *)a,
*bi = *(int * const *)b;
return (*ai > *bi) - (*ai < *bi);
}
int main (void) {
int n = 10;
int **arr = calloc (n, sizeof *arr);
if (!arr) {
perror ("calloc-arr");
return 1;
}
srand((unsigned int) time(NULL));
for (int i = 0; i < n; i++) {
if (!(arr[i] = calloc (1, sizeof *arr[i]))) {
perror ("calloc-arr[i]");
return 1;
}
*(arr[i]) = rand() % 1000;
}
for (int i = 0; i < n; i++)
printf (" %d", *(arr[i]));
putchar ('\n');
qsort (arr, 10, sizeof *arr, cmp_int_ptr);
for (int i = 0; i < n; i++) {
printf (" %d", *(arr[i]));
free (arr[i]); /* don't forget to free your int allocated */
}
putchar ('\n');
free(arr); /* now free pointers */
}
Example Use/Output
$ ./bin/qsortptrtoint
654 99 402 264 680 534 155 533 397 678
99 155 264 397 402 533 534 654 678 680
Look things over and let me know if you have questions.
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);
}
I try to implement some of the algorithms pure generic using C. I stick with the 3-way quicksort but somehow the implementation does not give correct output. The output nearly sorted but some keys aren't where it should be. The code is below. Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
static void swap(void *x, void *y, size_t size) {
void *tmp = malloc(size);
memcpy(tmp, x, size);
memcpy(x, y, size);
memcpy(y, tmp, size);
free(tmp);
}
static int cmpDouble(const void *i, const void *j) {
if (*(double *)i < *(double *)j)
return 1;
else if (*(double *)i == *(double *)j)
return 0;
else
return -1;
}
void qsort3way(void *base, int lo, int hi, size_t size,
int (*cmp)(const void *, const void *)) {
if (hi <= lo)
return;
else {
char *ptr = (char*)base;
char *v = ptr + lo * size;
int lt = lo, gt = hi;
int i = lo;
while (i <= gt) {
int c = cmp(v, ptr + i * size);
if (c < 0)
swap(ptr + (lt++) * size, ptr + (i++) * size, size);
else if (c > 0)
swap(ptr + i * size, ptr + (gt--) * size, size);
else
i++;
}
qsort3way(base, lo, lt - 1, size, cmp);
qsort3way(base, gt + 1, hi, size, cmp);
}
}
int main(void) {
int i;
double *d = (double*)malloc(sizeof(double) * 100);
for (i = 0; i < 100; i++)
d[i] = (double)rand();
qsort3way(d, 0, 100 -1, sizeof(double), cmpDouble);
for (i = 0; i < 100; i++)
printf("%.10lf\n", d[i]);
free(d);
return 0;
}
sample output:
41.0000000000
153.0000000000
288.0000000000
2082.0000000000
292.0000000000
1869.0000000000
491.0000000000
778.0000000000
1842.0000000000
6334.0000000000
2995.0000000000
8723.0000000000
3035.0000000000
3548.0000000000
4827.0000000000
3902.0000000000
4664.0000000000
5436.0000000000
4966.0000000000
5537.0000000000
5447.0000000000
7376.0000000000
5705.0000000000
6729.0000000000
6868.0000000000
7711.0000000000
9961.0000000000
8942.0000000000
9894.0000000000
9040.0000000000
9741.0000000000
After reading the book link that you provide to #JohnBollinger. I understand how your algorithm work. Your problem is that your pivot move, but you don't change the value of v. Your pivot is at the index lt
char *ptr = base;
int lt = lo, gt = hi; // lt is the pivot
int i = lo + 1; // we don't compare pivot with itself
while (i <= gt) {
int c = cmp(ptr + lt * size, ptr + i * size);
if (c < 0) {
swap(ptr + lt++ * size, ptr + i++ * size, size);
}
else if (c > 0)
swap(ptr + i * size, ptr + gt-- * size, size);
else
i++;
}
qsort3way(base, lo, lt - 1, size, cmp);
qsort3way(base, gt + 1, hi, size, cmp);
I propose you a "proper" solution:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef void qsort3way_swap(void *a, void *b);
typedef int qsort3way_cmp(void const *a, void const *b);
static void qsort3way_aux(char *array_begin, char *array_end, size_t size,
qsort3way_cmp *cmp, qsort3way_swap *swap) {
if (array_begin < array_end) {
char *i = array_begin + size;
char *lower = array_begin;
char *greater = array_end;
while (i < greater) {
int ret = cmp(lower, i);
if (ret < 0) {
swap(i, lower);
i += size;
lower += size;
} else if (ret > 0) {
greater -= size;
swap(i, greater);
} else {
i += size;
}
}
qsort3way_aux(array_begin, lower, size, cmp, swap);
qsort3way_aux(greater, array_end, size, cmp, swap);
}
}
static void qsort3way(void *array_begin, void *array_end, size_t size,
qsort3way_cmp *cmp, qsort3way_swap *swap) {
qsort3way_aux(array_begin, array_end, size, cmp, swap);
}
static void swap_int_aux(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
static void swap_int(void *a, void *b) { swap_int_aux(a, b); }
static int cmp_int_aux(int const *a, int const *b) {
if (*a < *b) {
return 1;
} else if (*a > *b) {
return -1;
} else {
return 0;
}
}
static int cmp_int(void const *a, void const *b) { return cmp_int_aux(a, b); }
static void print_int(char const *intro, int const *array, size_t const size) {
printf("%s:", intro);
for (size_t i = 0; i < size; i++) {
printf(" %d", array[i]);
}
printf("\n");
}
#define SIZE 42
int main(void) {
int array[SIZE];
srand((unsigned int)time(NULL));
for (size_t i = 0; i < SIZE; i++) {
array[i] = rand() % SIZE - SIZE / 2;
}
print_int("before", array, SIZE);
qsort3way(array, array + SIZE, sizeof *array, cmp_int, swap_int);
print_int("after", array, SIZE);
}
Note: The optimization int i = lo + 1; and char *i = array_begin + size; are mandatory. Because in the case where the function compare return that pivot != pivot this will lead to a infinite recursion. How this would be possible?
The function cmp is bug.
double has strange power... A double can be not equal to itself! (-nan).
The implementation does not give the correct result because it is wrong. Pretty badly wrong, in fact, given that it's supposed to be a three-way quicksort and not a regular one.
One basic problem is that you've omitted the bit where you move the pivot(s) into their proper position after the main partitioning loop. For standard quicksort, that requires one extra swap or assignment after the loop, depending on implementation details. For a three-way quicksort that involves one or two extra loops to move the potentially-many values equal to the pivot into their positions.
A more insidious problem is the one #Stargateur first pointed out: you track the pivot element by pointer, not by value, and you (sometimes) swap the original value out from that position in the course of the partitioning loop.
Furthermore, your main partitioning loop is wrong for a three-way quicksort, too. When you encounter an element equal to the pivot you just leave it in place, but you need instead to move it to one end or the other (or to some kind of auxiliary storage, if you're willing to incur that memory cost) so that you can perform that move to the middle at the end. In a sense, the previous problem is a special case of this one -- you're not reserving space for or tracking the pivot values. Fixing this will solve the previous problem as well.
I'm not sure what reference you used to prepare your implementation, or whether you built it from scratch, but Geeks for Geeks has a C++ (but pretty much also C) implementation for int arrays that you might want to check out.
Your implementation is incorrect because the pivot may move during the partitioning phase and you use a pointer for the comparison which no longer points to it. Implementations in other languages use the value of the pivot instead of its address.
Note also these shortcomings:
recursing both ways may cause stack overflow on pathological distributions. In you case, an array that is already sorted is a pathological distribution.
the comparison function should return the opposite values: -1 if a < b, +1 is a > b and 0 if a == b.
the API is non-standard and confusing: you should pass the number of elements instead of a range with included bounds.
Here is a corrected and commented version:
#include <stdio.h>
#include <stdlib.h>
static void swap(unsigned char *x, unsigned char *y, size_t size) {
/* sub-optimal, but better than malloc */
while (size-- > 0) {
unsigned char c = *x;
*x++ = *y;
*y++ = c;
}
}
void qsort3way(void *base, int n, size_t size,
int (*cmp)(const void *, const void *))
{
unsigned char *ptr = (unsigned char *)base;
while (n > 1) {
/* use first element as pivot, pointed to by lt */
int i = 1, lt = 0, gt = n;
while (i < gt) {
int c = cmp(ptr + lt * size, ptr + i * size);
if (c > 0) {
/* move smaller element before the pivot range */
swap(ptr + lt * size, ptr + i * size, size);
lt++;
i++;
} else if (c < 0) {
/* move larger element to the end */
gt--;
swap(ptr + i * size, ptr + gt * size, size);
/* test with that element again */
} else {
/* leave identical element alone */
i++;
}
}
/* array has 3 parts:
* from 0 to lt excluded: elements smaller than pivot
* from lt to gt excluded: elements identical to pivot
* from gt to n excluded: elements greater than pivot
*/
/* recurse on smaller part, loop on larger to minimize
stack use for pathological distributions */
if (lt < n - gt) {
qsort3way(ptr, lt, size, cmp);
ptr += gt * size;
n -= gt;
} else {
qsort3way(ptr + gt * size, n - gt, size, cmp);
n = lt;
}
}
}
static int cmp_double(const void *i, const void *j) {
/* this comparison function does not handle NaNs */
if (*(const double *)i < *(const double *)j)
return -1;
if (*(const double *)i > *(const double *)j)
return +1;
else
return 0;
}
int main(void) {
double d[100];
int i;
for (i = 0; i < 100; i++)
d[i] = rand() / ((double)RAND_MAX + 1);
qsort3way(d, 100, sizeof(*d), cmp_double);
for (i = 0; i < 100; i++)
printf("%.10lf\n", d[i]);
return 0;
}
I want to make scan_array and print_array function that can scan and print any basic data type array from stdin to stdout.
What I got so far:
#include<stdio.h>
void scan_array(void* base, size_t size_of_one, size_t n, const char* fmt)
{
char *element, *end;
end = (char *)base + size_of_one * n;
for (element = (char *)base; element < end; element += size_of_one) {
scanf(fmt, element);
}
}
void print_array(void* base, size_t size_of_one, size_t n, const char* fmt)
{
char *element, *end;
end = (char *)base + size_of_one * n;
for (element = (char *)base; element < end; element += size_of_one) {
printf(fmt, *element);
}
}
int main()
{
double a[3];
size_t n = 3;
scan_array(a, sizeof(double), n, "%lf");
int i;
for(i=0; i<n;i++) {
printf("%lf ", a[i]);
}
putchar('\n');
// prints zeros
print_array(a, sizeof(double), n, "%lf ");
return 0;
}
scan_array function works for all basic types, I checked that with normal for loop inside main.
print_array function works for INTS but not for any of other basic data types.
First thought was to change print_array def so that it takes function pointer instead of const char* fmt like this
void print_array(void* base, size_t size_of_one, size_t n, void (*data_printer)(void *el))
{
char *element, *end;
end = (char *)base + size_of_one * n;
for (element = (char*)base; element < end; element += size_of_one) {
data_printer(element);
}
}
And than make double_printer:
void double_printer(void *el)
{
printf("%lf ", * (double *) el);
}
And it works perfectly.
print_array(a, sizeof(double), n, &double_printer);
But is there any way to make print_array without function pointer?
Actually, it has some bugs regards to int.
Try input 127 128 255, it should return 127 -128 -1.
The problem is char *element, *end;, and then dereference with *element, it only reads 8 bits, not whole 32 bits regards of double.
For this very case, I think define a macro is a better option, or you need to provide the function pointer as qsort in c does.