1

I am trying to make a qsort type of function that has the same paramenters. I also wrote 3 functions to compare int, float and characters. For some reason it does not work in any case. I don't know whether this is a problem regarded my qsortx function or not, but I checked it several times and it should work perfectly fine. I am not sure what the problem is, or what I am doing wrong. I am currently learning the function pointers and I might not have got everything right related to it. Thanks in advance.

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

void qsortx(void*, int, int, int (*)(const void*, const void*));
int intcmp();
int floatcmp();
int charcmp();

int main()
{
    int i,n;
    char items[]={'c', 'a', 'b'};
    n = 3;
    for (i=0;i<n;++i) {
        printf("%c ", items[i]);
    }
    printf("\n");

    qsortx(items, n, sizeof(char), charcmp);

    for (i=0;i<n;++i) {
        printf("%c ", items[i]);
    }
    printf("\n");
    return 0;
}

void qsortx (void *tp, int length, int pace, int(*fp)(const void* a, const void* b)) {
    int switched,i,j;
    void *p;
    p=(void*)malloc(pace);
    switched = 1;
    while (1) {
        if (switched == 0) {
            return;
        }
        switched = 0;
        for (i=0; i<length-1;++i) {
            for (j=0;j<length-1;++j) {
                printf("%c %c", tp+i, tp+j);
                if (fp(tp+i, tp+j) > 0) {
                    memcpy(p, tp+i, pace);
                    memcpy(tp+i, tp+j, pace);
                    memcpy(tp+j, p, pace);
                    switched++;
                }
            }
        }

    }
}

int intcmp(const void* a, const void* b) {
    return *(int*)a - *(int*)b;
}

int floatcmp(const void* a, const void* b) {
    return *(float*)a - *(float*)b;
}

int charcmp(const void* a, const void* b) {
    return *(char*)a - *(char*)b;
}
Stefan
  • 43
  • 1
  • 8
  • Could you add an example where it does not work as you expect ? – n0p Nov 20 '14 at 15:29
  • If you run the code from above it writes out the same array out, it does not change the items in ascending order like it should: a b c. Also, sometimes it does not write out the first character, and if I test it on int with 5 elements in my array, then the first two numbers are going to be either huge numbers or zeroes. – Stefan Nov 20 '14 at 15:31
  • One thing I see immediately is that the array _items_ in _main_ is an array of _int_, but you send _sizeof(char)_. – Thomas Padron-McCarthy Nov 20 '14 at 15:36
  • In addition to the size issue @Thomas pointed out, on your first pass through the loop in `qsortx()` you check the value of variable `switched` before it has been initialized. – John Bollinger Nov 20 '14 at 15:40
  • I noticed those two problems myself after submitting the question, but it still does not work. – Stefan Nov 20 '14 at 15:41
  • Moreover, I'm supposing you mean to set `switched` when you perform an element swap, but you do not do so. – John Bollinger Nov 20 '14 at 15:41
  • Note, too, that arithmetic on void pointers is **illegal** in C (see http://stackoverflow.com/questions/3523145/pointer-arithmetic-for-void-pointer-in-c). Some compilers permit it as an extension, as if a `void *` were equivalent to `char *`, but if you want that then you would be best off to be explicit about it. – John Bollinger Nov 20 '14 at 15:44
  • Ok, but I have casted the void * a and void * b to chars so in theory it should work. – Stefan Nov 20 '14 at 15:49
  • 1
    Additionally, your expressions of the form `tp+i` are incorrect. They probably do the right thing by happenstance when the items being sorted have length 1, but otherwise not. – John Bollinger Nov 20 '14 at 15:51
  • `return *(int*)a - *(int*)b;` is a problem should overflow occur. Suggest `return (*(int*)a > *(int*)b) - (*(int*)a < *(int*)b);` – chux - Reinstate Monica Nov 20 '14 at 16:18
  • @John Bollinger Hmmm Do not see a _functional_ difference between our 2 equations. IAC, OP's `qsortx()` only uses a `> 0` or not, so the compare functions could be `return *(int*)a > *(int*)b;`. – chux - Reinstate Monica Nov 20 '14 at 16:38
  • this line: if (fp(tp+i, tp+j) > 0) { should be: if (fp(tp+i, tp+j) != 0) { – user3629249 Nov 20 '14 at 21:01
  • your code is a messed up insertion sort. Not a quicksort – user3629249 Nov 20 '14 at 21:06

3 Answers3

2

You have multiple problems related to pointer arithmetic and element sizes. You also have a logic error in your sort (which I guess you know is a unidirectional shaker sort). Here's a version of the qsortx() function that fixes these deficiencies:

void qsortx (void *tp, int length, int pace, int(*fp)(const void* a, const void* b)) {
    if (length > 1) {
        char *bound = ((char *) tp) + (length * pace);
        char *p = malloc(pace);
        char *item1p;

        for (item1p = tp; item1p < (bound - pace); item1p += pace) {
            char *item2p;

            for (item2p = item1p + pace; item2p < bound; item2p += pace) {
                if (fp(item1p, item2p) > 0) {
                    memcpy(p, item1p, pace);
                    memcpy(item1p, item2p, pace);
                    memcpy(item2p, p, pace);
                }
            }
        }

        free(p);
    }
}

Note that:

  1. All pointer arithmetic is performed on values of type char *.
  2. The element size (pace) must be taken into account as you step through the input array, else you just scramble your data.
  3. The innermost loop should start at the element after the one being considered in the next-outer loop.
  4. switched = 1 is a better choice than switched ++ because it cannot overflow, and all you care about is zero vs. non-zero. (Update: but switched is no longer relevant.)
  5. (Update) It is incorrect to exit early in the event that a pass through the item1p loop results in zero swaps. Just because one element is already in its correct place does not mean that all the subsequent elements are also in their correct places. I updated my code above to remove that behavior.
  6. (Update) As chux observed, the temporary space reserved for swapping elements was not freed. I added an appropriate free(p).
  7. (Update) I also made sorting conditional on the array length being greater than 1, which avoids undefined behavior associated with bound - pace in the event that the length is zero.
John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • For some reason it gets into a infinite loop, as it takes the item1p as the whole array and not only the first element, and the item2p as the whole array except the first element, at least that what it shows in the debug. Also, even after the item1p gets to abc, it still comes out as true and starts sorting again. – Stefan Nov 20 '14 at 16:21
  • The code I presented runs correctly for me with the `main()` and `charcmp()` functions you provided. – John Bollinger Nov 20 '14 at 16:24
  • Oh, sorry, I checked it again and I called the wrong function. Thanks a lot. – Stefan Nov 20 '14 at 16:28
  • Good catch, @chux. Updated. – John Bollinger Nov 20 '14 at 16:54
  • Note that this function invokes undefined behavior if `pace` is less than zero and length is greater than zero. Since it's nonsensical for `pace` to be less than one, though, I don't really care. That problem could be avoided by giving `pace` an unsigned type (`size_t` would be natural), but I'm sticking with the function signature specified in the question. – John Bollinger Nov 20 '14 at 17:04
  • @John Agree with your "pace is less than zero" concern and "size_t" idea. Could use `if (length > 1 && pace > 0) {`. Yet we are walking down a pedantic pathway anyways as `length * pace` may overflow regardless of types used. Still using good types and some range checking is good as this answer has done. +1 – chux - Reinstate Monica Nov 20 '14 at 17:17
0

here is the pseudo code and implementation of the quicksort (qsort) algorithm, with some accessory code, as defined in the http://www.codingbot.net/2013/01/quick-sort-algorithm-and-c-code.html web page: Note that this algorithm is slightly different from qsort() in that there is a different parameter list and certain other details. However, the basic algorithm is the same.

function quicksort('array')
    if length('array') ≤ 1
        return 'array'  // an array of zero or one elements is already sorted
        select and remove a pivot value 'pivot' from 'array'
        create empty lists 'less' and 'greater'
        for each 'x' in 'array'
            if 'x' ≤ 'pivot' 
                then append 'x' to 'less'
            else 
                append 'x' to 'greater'
            endif
        end for
        return concatenate(quicksort('less'), 'pivot', quicksort('greater') );

notice that qsort is a partition sort, using recursion.

#include<stdio.h>
#include<conio.h>

void quick_sort(int arr[20],int,int);

int main()
{
   int arr[20],n,i;
   clrscr();
   printf("Enter the number of elements in the Array: ");
   if( 1 != scanf(" %d",&n) ) 
   {
       perror( "scanf for count of elements" );
       exit(1);
   }

   printf("\nEnter %d elements:\n\n",n);

   for(i=0 ; i<n ; i++)
   {
        printf(" Array[%d] = ",i);
        if( 1 != scanf(" %d",&arr[i]) )
        {
            perror( "scanf for element values" );
            exit(2);
        }

   }

   quick_sort(arr,0,n-1);
   printf("\nThe Sorted Array is:\n\n");

   for(i=0 ; i<n ; i++)
   {
        printf(" %4d",arr[i]);
   }
   getch();
}

void quick_sort(int arr[20],int low,int high)
{
    int pivot; // used in partitioning the array
    int j; // loop index
    int temp; // for swapping
    int i; // loop index

    if(low<high)
    {
        pivot = low; 
        i = low;
        j = high;

        while(i<j)
        {
            // find next item not in proper sequence
            while((arr[i] <= arr[pivot]) && (i<high))
            {
                i++;
            }

            // find next item not in proper sequence
            while(arr[j] > arr[pivot])
            {
                j--;
            }

            // following is where a callback function would be invoked
            if(i<j)
            { 
                temp=arr[i];
                arr[i]=arr[j];
                arr[j]=temp;
            }
        }

        temp=arr[pivot];
        arr[pivot] = arr[j];
        arr[j]=temp;

        // following is where recursion is used to perform sort on sub partitions
        quick_sort(arr,low,j-1);
        quick_sort(arr,j+1,high);
   }
}
user3629249
  • 16,402
  • 1
  • 16
  • 17
0

this is a much better algorithm for your purposes. however, it only handles integers, so you would need to add the comparison function as a 4th parameter to quicksort() and modify the code to use your comparison function

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

void swap(int *x,int *y);
int choose_pivot(int i,int j );
void quicksort(int list[],int m,int n);
void display(int list[],const int n);

int main()
{
    const int SIZE = 10;
    int list[SIZE];

    int i = 0;

    /* generates random numbers and fill the list */
    for(i = 0; i < SIZE; i++ )
    {
        list[i] = rand();
    }

    printf("The list before sorting is:\n");
    display(list,SIZE);

    /* sort the list using quicksort algorithm */
    quicksort(list,0,SIZE-1);

    printf("The list after sorting:\n");
    display(list,SIZE);
}


void swap(int *x,int *y)
{
    // for integer swaps, 3 exclusive OR operations would be much faster
    // and not require a temp variable
    int temp;
    temp = *x;
    *x = *y;
    *y = temp;
}


int choose_pivot(int i,int j )
{
    return((i+j) /2);
}


void quicksort(int list[],int m,int n)
{
    int key,i,j,k;

    if( m < n)
    {
        k = choose_pivot(m,n);
        swap(&list[m],&list[k]);
        key = list[m];
        i = m+1;
        j = n;
        while(i <= j)
        {
            while((i <= n) && (list[i] <= key))
            {
                 i++;
            }

            while((j >= m) && (list[j] > key))
            {
                j--;
            }

            if( i < j)
            {
                swap(&list[i],&list[j]);
            }
        }

        /* swap two elements */
        swap(&list[m],&list[j]);

        /* recursively sort the lesser list */
        quicksort(list,m,j-1);
        quicksort(list,j+1,n);
    }
}


void display(int list[],const int n)
{
    int i;

    for(i=0; i<n; i++)
    {
        printf("%d\t",list[i]);
    }
}
user3629249
  • 16,402
  • 1
  • 16
  • 17