Having trouble getting my head around implementing the qsort() built into C to sort an array of structs by a stored int value (hitCount).
My struct:
typedef struct words {
const char *word;
int hitCount;
} words;
I'm trying to use the example given by Microsoft (http://support.microsoft.com/kb/73853).
So I've got at the top:
typedef int (*compfn)(const void*, const void*);
and the comparision method:
int compare (words *a, words *b) {
if (a->hitCount > b->hitCount) {
return -1;
} else if (a->hitCount < b->hitCount) {
return 1;
} else {
return 0;
}
}
then within another method I call qsort with my array name and other details replacing the Microsoft example:
qsort((void *) &output, outputLength, sizeof(words), (compfn)compare);
This gives a segmentation fault.
I don't fully understand how to use qsort so I assume where I've adapted it from Microsoft's example I've done it incorrectly.
I hope I've included the mistake and can get some enlightenment as to what I should be doing in order for this to work correctly.
Many Thanks!
You have to pass the array not the address of the array to qsort.
qsort( output, ... );
Also your compare function must return an int and accept two const void* arguments.
Casting your function int compare (words *a, words *b) to a different( yet correct ) type which is then called by qsort() will cause undefined behaviour.
The compare function must be:
int compare (const void *a, const void *b)...
Then you cast a and b to correct types:
((words*)a)->hitCount < ((words*)b)->hitCount
I suspect that outputLength is computed incorrectly. A complete working example:
#include <stdio.h>
#include <stdlib.h>
typedef struct words {
const char *word;
int hitCount;
} words;
int compare(const void * left, const void * right) {
const words * a = (const words *) left;
const words * b = (const words *) right;
if (a->hitCount > b->hitCount) {
return -1;
} else if (a->hitCount < b->hitCount) {
return 1;
} else {
return 0;
}
}
int main() {
struct words output[] = {
{ "hello", 314 },
{ "world", 42 },
{ "answer", 42 }
};
int outputLength = sizeof(output) / sizeof(output[0]);
int i;
output[0].word = "hello";
output[0].hitCount = 314;
output[1].word = "world";
output[1].hitCount = 42;
qsort(output, outputLength, sizeof(words), compare);
for (i = 0; i < outputLength; ++i) {
printf("%d %s\n", output[i].hitCount, output[i].word);
}
return 0;
}
The prototype of the standard library function qsort is
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
Note the signature of the compare function. You cannot typecast a pointer to a function of different signature and make it work correctly. Therefore, typecasting your compare function will not work. It must have the same signature as declared in the prototype of qsort. Change your compare function to -
int compare(const void *a, const void *b) {
int c = ((words *) a)->hitCount;
int d = ((words *) b)->hitCount;
if(c > d) return -1;
if(c < d) return 1;
return 0;
}
The first argument base of qsort is the base address of the buffer which contains the elements to be sorted. Also, any pointer type is assignment compatible to a void * variable and as such you don't need to cast the base address. Therefore, you should call the qsort function as -
qsort(output, outputLength, sizeof output[0], compare);
Got it working with:
int compare (const void *a, const void *b) {
if (((words *)a)->hitCount > ((words *)b)->hitCount) {
return -1;
} else if (((words *)a)->hitCount < ((words *)b)->hitCount) {
return 1;
} else {
return 0;
}
}
and call to sort:
qsort(output, outputLength, sizeof(words), compare);
Thanks to everyone's help but majority credit to "self".
Related
I am trying to use qsort to sort a struct containing pointers. Is the problem with the comparison function? How do I fix so I can sort based on the cc.
Here is the code:
#include <stdlib.h>
#include <string.h>
typedef enum {
PETROL,
DIESEL,
ELECTRIC,
LPG,
BIOFUEL,
OTHER
} fuel_t;
typedef struct car_tag {
unsigned cc;
fuel_t fueltype;
} car_t;
typedef struct fleet_tag {
car_t ** cars;
size_t n_cars;
} fleet_t;
int car_comp(const void * vp1, const void * vp2) {
const car_t* const c1 = vp1;
const car_t* const c2 = vp2;
if (c1->cc > c2->cc)
return -1;
else if (c1->cc < c2->cc)
return 1;
else {
return 0;
}
}
int main() {
car_t array[] = {
{ 600, PETROL},
{1200, PETROL},
{1000, PETROL},
{1600, DIESEL},
{1000, ELECTRIC}
};
int size = sizeof(array) / sizeof(array[0]);
fleet_t fl;
fl.n_cars = size;
fl.cars = malloc(size * sizeof(car_t));
for (int i = 0; i < size; i++) {
car_t* pc = malloc(sizeof(car_t));
memcpy(pc, &array[i], sizeof(car_t));
fl.cars[i] = pc;
}
// how to sort cars by cc
qsort(&fl, fl.n_cars, sizeof(car_t), car_comp);
// sort function doesn't correctly sort fleet of cars by cc
}
I don't see the need for the dynamic allocation and memcpy invoke for each to-be-sorted car in this code at all.
You're building a pointer bed (a sequence of pointers) so why not just allocate that (which you're doing), and then store the addresses of each element from array there. Then, tailor your comparator to address what you're sending: an address of a pointer (pointer to pointer) and setup the dereferences accordingly
Add to that, you should be passing fl.cars to qsort, not &fl, and the sizeof argument therein is also wrong.
Finally, I don't know if you intentionally wanted to use a greater-than logic stack in your comparator, but that is exactly what you ended up with.
Example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum {
PETROL,
DIESEL,
ELECTRIC,
LPG,
BIOFUEL,
OTHER
} fuel_t;
typedef struct car_tag {
unsigned cc;
fuel_t fueltype;
} car_t;
typedef struct fleet_tag {
car_t ** cars;
size_t n_cars;
} fleet_t;
int car_comp(const void * vp1, const void * vp2)
{
const car_t * const *pc1 = vp1;
const car_t * const *pc2 = vp2;
if ((*pc1)->cc > (*pc2)->cc)
return -1;
if ((*pc1)->cc < (*pc2)->cc)
return 1;
return 0;
}
int main() {
car_t array[] = {
{ 600, PETROL},
{1200, PETROL},
{1000, PETROL},
{1600, DIESEL},
{1000, ELECTRIC}
};
int size = sizeof(array) / sizeof(array[0]);
fleet_t fl;
fl.n_cars = size;
fl.cars = malloc(size * sizeof *fl.cars);
for (int i = 0; i < size; i++)
fl.cars[i] = array+i;
// how to sort cars by cc
qsort(fl.cars, fl.n_cars, sizeof *fl.cars, car_comp);
for (int i=0; i<size; ++i)
printf("%d (%u, %d)\n", i+1, fl.cars[i]->cc, fl.cars[i]->fueltype);
free(fl.cars);
return EXIT_SUCCESS;
}
Output
1 (1600, 1)
2 (1200, 0)
3 (1000, 0)
4 (1000, 2)
5 (600, 0)
qsort works by feeding it a sequence of "things", a length stating how many "things" there are, a size noting how big each "thing" in the sequence is, and finally a comparator function which will be fed the address of each "thing" during execution of the algorithm.
In your case, your "things" are pointers to car_t structures. In fact,
Your sequence is a dynamic array of pointers; your "thing" is a pointer to a car_t.
You length is size.
Your size of each "thing" is the size of a pointer.
Your comparator will access the address of two of your things (therefore, two pointers, so pointers to pointers), and act accordingly.
Therefore, the call becomes:
qsort(fl.cars, fl.n_cars, sizeof *fl.cars, car_comp);
Finally, note that the original array remains unchanged. The sort modified your pointer bed only. That was probably desirable, and I hope you understand how it works.
Yesterday I had a test on C where I coudn't figure out the last question:
We were given two arrays of two types of arrays: arrays including consecutive elements that are equal(eg: {"stack","heap","heap"}) and arrays of where no consecutive elements where equal (eg: {1,2,3,4,5,6,7,8,9}).
We were then asked to find one function that returned 1 or 0 if the given array contained doubles or not. So this function had to work with both integer arrays and char * arrays.
This is what I came up with today (but it keeps giving the wrong answer and crashing afterwards or a segmentation fault when comparing the strings)
Edit: correct code (thanks to #BLUEPIXY !)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int contains_dup(void *array, size_t size, size_t sizeoftype, int (*cmp)(const void*, const void*)){
//array != NULL, size != 0
char *obj = array;
size_t i;
for(i = 0; i < size-1; ++i){
if(cmp(obj + sizeoftype * i, obj + sizeoftype * (i+1)))
return 1;
}
return 0;
}
int eqi(const void *a, const void *b){
int x = *(const int *)a;
int y = *(const int *)b;
return x == y;
}
int eqs(const void *a, const void *b){
return strcmp(a, b) == 0;
}
#define TEST(name, cmp)\
do{\
int test;\
puts(#name ":");\
test = contains_dup(name, sizeof(name)/sizeof(*name), sizeof(*name), cmp);\
test ? puts("doubles? Yes\n") : puts("doubles? No\n");\
}while(0)\
/**/
int main(void){
int ints_yes[] = {0,1,2,2,2,3,4,4,5};
int ints_no[] = {0,1,2,3,4,5,6,7,8};
char *strings_yes[]={"heap","stack","stack","overflow"};
char *strings_no[] ={"heap","stack","heap","stack","overflow"};
puts("test:");
TEST(ints_yes, eqi);
TEST(ints_no, eqi);
TEST(strings_yes, eqs);
TEST(strings_no, eqs);
return 0;
}
Wrong old code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int array_contains_doubles(void ** array, int size, int sizeoftype){
int i;
char **out =(char**) malloc(size * sizeof(char*));
for(i=0;i<size;i++){ //trying to convert the array of ints to an
out[i] = array+i*sizeoftype; //array of char * eg: {1,2} ->{"1","2"}
// *out[i] +='a';
printf("%c\n",*out[i]);
}
out[i]= NULL;
while(*(out+1)!=NULL){
if(strcmp(*out,*(out++))==0){ //<- where i get the segmentation error
return 1;
}
}
return 0;
}
int main(void){
int i;
int ints_yes[] = {0,1,2,2,2,3,4,4,5};
int ints_no[]={0,1,2,3,4,5,6,7,8};
char * strings_yes[]={"heap","stack","stack","overflow"};
char * strings_no[]={"heap","stack","heap","stack","overflow"};
int test = array_contains_doubles((void **) ints_no,
sizeof(ints_no)/sizeof(ints_no[0]), sizeof(int));
(test) ? (printf("doubles? Yes")) : (printf("doubles? No"));
}
Sorry for any spelling mistakes, english is not my native language.
What your teacher is likely fishing for, is for you to implement a "functor" similar to the function pointer passed to bsearch (study this function). Something along the lines of this:
typedef int comp_func_t (const void*, const void*);
bool equal (const void* obj1, const void* obj2, comp_func_t* comp)
{
return comp(obj1, obj2)==0;
}
You call equal from your application with a pointer to the objects to compare, no matter what kind of objects they are. The function pointer specifies how objects of this type should be compared. You then implement the comparison functions for each type:
int comp_int (const void* obj1, const void* obj2)
{
int a = *(const int*)obj1;
int b = *(const int*)obj2;
if(a < b)
{
return -1;
}
else if(a > b)
{
return 1;
}
else // a == b
{
return 0;
}
}
int comp_str (const void* obj1, const void* obj2)
{
...
}
Typical use could be:
int x;
int y;
...
if(equal(&x, &y, comp_int))
{
...
}
Now this only compares two objects, so you'll have to expand this for an array by 1) sorting the array and 2) calling it for every two adjacent items in the sorted array, to find out if any are equal.
The above is the old, "de facto standard" way to implement type-specific behavior in C. In newer versions of the language, more elegant ways are available through the _Generic keyword, but this would probably not be addressed on a beginner-level class.
I'm trying to sort a 2d array of strings a simplified version looks like,
( I dont want to change the datatype of "nameArray" to "char *nameArray[4]" )
#include <sys/types.h>
#include <stdio.h>
int cstring_cmp(const void *a, const void *b)
{
const char **ia = (const char **)a;
const char **ib = (const char **)b;
return strcasecmp(*ia, *ib);
}
int Test()
{
char nameArray[4][10]={"test","alpha","Hyper","city"};
// int nElem = sizeof(nameArray)/sizeof(char *);
int index = 0;
//printf("nElem =%d\n", nElem);
for(index=0; index < 4; index++)
{
printf("-> %s\n", nameArray[index]);
}
qsort( &nameArray[0], 4, sizeof(nameArray[0]), cstring_cmp);
printf("After sort\n");
for(index=0; index < 4; index++)
{
printf("-> %s\n", nameArray[index]);
}
return 0 ;
}
(
UPDATE: changed so that I'm directly using the value(4) instead of calculating nElem. My problem is getting the qsort to working. )
The arguments to the comparison function are just pointers, rather than pointers to pointers.
You also don't need to cast I believe, since the parameters are void *, you can just assign them to the local variables and the compiler takes care of them.
Try this:
int cstring_cmp(const void *a, const void *b)
{
const char *ia = a;
const char *ib = b;
return strcasecmp(ia, ib);
}
Or even get rid of the local variables if you don't need them (only need them if you're planning on adding more code to the comparison function):
int cstring_cmp(const void *a, const void *b)
{
return strcasecmp(a, b);
}
int cstring_cmp(const void *a, const void *b)
{
return strcmp(a, b);
}
I am trying to sort an array of pointers to structs (definition below) based on the value stored in the void* of the "bucket" struct that I know are ints. It compiles and prints out my array of buckets and their values just fine with no errors or warnings but it isn't actually sorting the array. I have used asserts to try to find anywhere that could cause an error with qsort.
Struct definitions:
typedef struct _bucket{
void* val;
char *word;
}bucket;
typedef struct _root{
bucket **list;
int hashTableLength;
}root;
Sort Function to be passed to the qsort function:
int sortFunc(const void *a, const void *b){
bucket *bucketA=(bucket*)a;
bucket *bucketB=(bucket*)b;
int bucketAVal = *((int*)bucketA->val);
int bucketBVal = *((int*)bucketB->val);
assert((bucketAVal&&bucketBVal)!=0);
return bucketAVal-bucketBVal;
}
Sort the array and print:
void sort(root* inRoot, int(*sortFunc)(const void *a, const void *b)){
int length = inRoot->hashTableLength;
assert(length==11); //known length of hash array
for (int i = 0; i<length; i++)
assert(inRoot->list[i] != NULL);
qsort(inRoot->list, length, sizeof(bucket*), sortFunc);
for(int i =0; i<length; i++)
printf("%s was found %d times\n", inRoot->list[i]->word, *((int*)(inRoot->list[i]->val)));
return;
}
The compare function sortFunc() receives a pointer to each object. The array inRoot->list is an array of bucket * so sortFunc() is receiving pointers to bucket *: bucket **.
Also the int subtraction is subject to possible overflow. Use the idiomatic 2 compares to solved that.
int sortFunc(const void *a, const void *b) {
bucket **bucketA = (bucket**) a;
bucket **bucketB = (bucket**) b;
void *vA = (*bucketA)->val;
void *vB = (*bucketB)->val;
int iA = *((int*) vA);
int iB = *((int*) vB);
return (iA > iB) - (iA < iB);
}
I am trying to sort a struct run array called results by a char, but when I print the array, nothing is sorted. Have a look at this:
struct run {
char name[20], weekday[4], month[10];
(And some more...)
};
typedef struct run run;
int name_compare(const void *a, const void *b)
{
run *run1 = *(run **)a;
run *run2 = *(run **)b;
return strcmp(run1->name, run2->name);
}
int count_number_of_different_persons(run results[])
{
int i = 0;
qsort(results, sizeof(results) / sizeof(run), sizeof(run), name_compare);
for(i = 0; i <= 999; i++)
{
printf("%s\n", results[i].name);
}
// not done with this function yet, just return 0
return 0;
}
The output from the above is just a list of names in the order they were originally placed
int count_number_of_different_persons(run results[])
This doesn't really let you use sizeof on the array, because array is decayed to pointer.
This
run *run1 = *(run **)a;
also looks weird, shouldn't it be
run *run1 = (run*)a;
?
One problem is in name_compare. Try this instead:
int name_compare(const void *a, const void *b)
{
run *run1 = (run *)a;
run *run2 = (run *)b;
return strcmp(run1->name, run2->name);
}
Check the following code:
As #michel mentioned, sizeof(array) provides size of the pointer, not the size of the array itself, as while passing array it is treated as a pointer. Hence either send the number of elements to the function count_number_of_different_persons or define a MACRO of number of elements. Hope this helps. :).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NOE 3
struct run
{
char name[20];
};
typedef struct run run;
int name_compare (const void *a, const void *b )
{
return strcmp (((run *)a)->name, ((run *)b)->name);
}
int count_number_of_different_persons(run results[], int noOfElements)
{
int i=0;
qsort(results, noOfElements, sizeof (run), name_compare);
for (i=0; i<noOfElements; i++)
printf ("%s\n",results[i].name);
}
int main ( int argc, char * argv[])
{
run a, b, c;
run arg[NOE];
strcpy (a.name, "love");
strcpy (b.name, "you");
strcpy (c.name, "i");
arg[0] = a;
arg[1] = b;
arg[2] = c;
count_number_of_different_persons(arg, sizeof(arg)/sizeof(run));
};