I am trying to sort array of structs using qsort() but frustratingly, it's not working. I have read the manpage for qsort() and I think I have the comparator function that syntactically looks okay, but when I print the "sorted" array after calling qsort(), nothing is sorted in my array.
The code:
#include <stdlib.h>
#include <stdio.h>
#define ARRAY_SZ 5
typedef struct SingleChar
{
unsigned char Character;
unsigned int Weight;
} *SingleCharPtr;
int CompareWeights(const void *a, const void *b)
{
const SingleCharPtr p1 = (SingleCharPtr)a;
const SingleCharPtr p2 = (SingleCharPtr)b;
// printf("Weight1: %u\tWeight2: %u\n", p1->Weight, p2->Weight);
// return (p1->Weight - p2->Weight);
if (p1->Weight < p2->Weight)
return -1;
else if (p1->Weight > p2->Weight)
return 1;
else
return 0;
}
SingleCharPtr MakeChar(unsigned char c, unsigned int w)
{
SingleCharPtr scptr = malloc(sizeof(struct SingleChar));
if (!scptr)
{
fprintf(stderr, "[Error] Out of memory\n");
exit(1);
}
scptr->Character = c;
scptr->Weight = w;
return scptr;
}
int main(void)
{
SingleCharPtr *chars = malloc(ARRAY_SZ * sizeof(SingleCharPtr));
chars[0] = MakeChar('B', 3);
chars[1] = MakeChar('E', 7);
chars[2] = MakeChar('A', 4);
chars[3] = MakeChar('D', 6);
chars[4] = MakeChar('C', 2);
qsort(chars, ARRAY_SZ, sizeof(SingleCharPtr), &CompareWeights);
int i;
for (i = 0; i < ARRAY_SZ; i++)
{
printf("Character: %c\tWeight: %u\n", chars[i]->Character, chars[i]->Weight);
free(chars[i]);
}
free(chars);
return 0;
}
Also, in the comparator function (CompareWeights()), I found out that when I print the weight of the structs pointed by SingleCharPtr, I get 0s for all of them.
Any pointer to right direction would be highly appreciated.
The problem: qsort() passes in pointers to the elements to be compared to the comparator function, and not the elements themselves. So, the arguments to your CompareWeights() function are actually const SingleCharPtr *, disguised as const void *. What you should do in that function is:
const SingleCharPtr p1 = *(const SingleCharPtr *)a;
etc.
Sidenotes:
I. If your assumption had been valid, then you wouldn't have needed the cast:
const SingleCharPtr p1 = a;
is preferred over
const SingleCharPtr p1 = (SingleCharPtr)a;
because of this.
II. The comparison function need not return -1, 0 or 1. It should return an integer less than 0, 0 or greater than 0. Thus, all the huge if in CompareWeight() is completely superfluous, write
return p1->Weight - p2->Weight;
instead.
III. SingleCharPtr *chars = malloc(ARRAY_SZ * sizeof(SingleCharPtr)); - Why? You only use the chars array locally in the main() function, you don't need dynamic allocation for that. Why not write
SingleCharPtr chars[ARRAY_SZ];
instead?
If you see the example in e.g. this manual page, you will see that the when qsort is passed an array of pointer (just like you have) then the arguments to the sorting function are actually pointers to pointers. This is because qsort passes pointers to the elements, not the elements themselves.
To accomodate for that, change accordingly:
int CompareWeights(const void *a, const void *b)
{
const SingleCharPtr p1 = *(SingleCharPtr*)a;
const SingleCharPtr p2 = *(SingleCharPtr*)b;
return (p1->Weight - p2->Weight);
}
Related
My problem is that qsort seems to be sending weird pointers to the comparator function. The comparator function itself seems to be working fine if I create 2 gaps and send their pointers as arguements. However, when debugging I'm getting wrong values, even though the gap array is initialized correctly.
I am running the code on Windows 10 if that matters.
Gap definition and comparator function:
typedef struct open_space_t{
ssize_t size;
off_t start;
}Gap;
int GapComparator(const void * aa, const void * bb){
ssize_t a = ((Gap*) aa)->size;
ssize_t b = ((Gap*) bb)->size;
if(a>b){
return 1;
}
if(b>a){
return -1;
}
else{
return 0;
}
}
Running qsort:
Gap** allGaps = malloc((2) * sizeof(*allGaps));
allGaps[0] = malloc(sizeof(*allGaps[0]));
allGaps[0]->size = 20;
allGaps[0]->start = 30044;
allGaps[1] = malloc(sizeof(*allGaps[0]));
allGaps[1]->size = 20;
allGaps[1]->start = 30064;
qsort(allGaps, 2, sizeof(*allGaps), GapComparator);
The comparator receives pointers to the elements it should compare. Your array elements are pointers (Gap *). Therefore the comparator receives Gap **.
Fix:
int GapComparator(const void * aa, const void * bb){
ssize_t a = (*(Gap**) aa)->size;
ssize_t b = (*(Gap**) bb)->size;
Or alternatively:
int GapComparator(const void *aa, const void *bb) {
const Gap **pa = aa, **pb = bb;
ssize_t a = (*pa)->size;
ssize_t b = (*pb)->size;
Here is the compare function:
int compare(const void *a, const void *b) {
char* *s = (char* *) a;
char* *t = (char* *) b;
return sort_order * strcmp(*s, *t); // sort_order is -1 or 1
}
Now my question is what is the reasoning behind casting to a double pointer of a particular type? Or rather, why is the double pointer cast needed and how is it used internally?
Other variables used:
char **wordlist; int nbr_words; (array elements are) char *word;
Ex qsort call: qsort(wordlist, nbr_words, sizeof(char *), compare);
It would help if you showed the definition of wordlist, but most likely it's defined as a char **. The compare() function receives a pointer to each element of your list. If each element of your list is of type char *, then compare() is going to receive two pointers to char *, or two char ** in other words.
The conversion to char ** (note that an actual cast would be superfluous, in this particular case, if you weren't going from a const void pointer, to a non-const char **) itself is necessary because qsort() has to work on any kind of type, so the arguments get converted to void * before they are passed. You can't deference a void * so you have to convert them back to their original types before doing anything with them.
For instance:
#include <stdio.h>
int compare_int(void * a, void * b) {
int * pa = a;
int * pb = b;
if ( *pa < *pb ) {
return -1;
} else if ( *pa > *pb ) {
return 1;
} else {
return 0;
}
}
int compare_double(void * a, void * b) {
double * pa = a;
double * pb = b;
if ( *pa < *pb ) {
return -1;
} else if ( *pa > *pb ) {
return 1;
} else {
return 0;
}
}
int compare_any(void * a, void * b, int (*cfunc)(void *, void *)) {
return cfunc(a, b);
}
int main(void) {
int a = 1, b = 2;
if ( compare_any(&a, &b, compare_int) ) {
puts("a and b are not equal");
} else {
puts("a and b are equal");
}
double c = 3.0, d = 3.0;
if ( compare_any(&c, &d, compare_double) ) {
puts("c and d are not equal");
} else {
puts("c and d are equal");
}
return 0;
}
Outputs:
paul#local:~/src/c/scratch$ ./comp
a and b are not equal
c and d are equal
paul#local:~/src/c/scratch$
The compare_any() function will compare any type which is supported, in this case, int and double, because we can pass a function pointer to it. However, the signature of the passed function must be the same, so we can't declare compare_int() to take two int * arguments, and compare_double() to take two double *. We have to declare them both as taking two void * arguments, and when we do this, we have to convert those void * arguments to something useful within those functions before we can work with them.
What's happening in your case is exactly the same, but the data themselves are pointers, so we're passing pointers to pointers, and so we need to convert void * to, in your case, char **.
EDIT: To explain some confusion in the comments to the original question about how qsort() works, here's the qsort() signature:
void qsort(void *base, size_t nmemb, size_t size,
int(*compar)(const void*, const void*))
base is a pointer to the first element of an array, nmemb is the number of members of that array, and size is the size of each element.
When qsort() calls compar on, say, the first and second elements of your array, it'll send the address of the first element (i.e. base itself) and the address of the element (i.e. base + size).
If base was originally declared as an array of int, then the compare function must interpret those pointers it receives as pointers to int, as int *. If base was originally declared as an array of strings, as a char **, then the compare function must interpret those pointers as pointers to char *, i.e. as char **.
In all cases, the compare function gets pointers to elements. If you have an int array, then you must interpret those pointers as int * in your compare function. If you have a char * array, then you must interpret them as char **, and so on.
In this case, you obviously could call strcmp() if you just passed plain char * arguments to the compare function. But, because qsort() is generic it can only pass pointers to the compare function, it can't actually pass the value of your elements - it's the use of void * which allows it to be generic, because any type of object pointer can be converted to void *, but there is no equivalent datatype to which any non-pointer value can be converted. For that reason, it has to work the same way with regular types like int and double, with pointers, and with structs, and the only way to get it to work correctly with all possible types is to have it deal with pointers to elements, not with the elements themselves, even when the elements are themselves also pointers. For this reason, it might seem like you're getting an unnecessary level of indirection, here, but it actually is necessary in order for qsort() to be able to function in the generic way that it does.
You can see this more clearly if I modify the code above so that compare_any() is more similar to qsort(), and takes not two pointers, but a single pointer to a two-element array of various types (slightly contrived example, but we're keeping it simple):
#include <stdio.h>
#include <string.h>
int compare_int(void * a, void * b) {
int * pa = a;
int * pb = b;
if ( *pa < *pb ) {
return -1;
} else if ( *pa > *pb ) {
return 1;
} else {
return 0;
}
}
int compare_double(void * a, void * b) {
double * pa = a;
double * pb = b;
if ( *pa < *pb ) {
return -1;
} else if ( *pa > *pb ) {
return 1;
} else {
return 0;
}
}
int compare_string(void * a, void * b) {
char ** pa = a;
char ** pb = b;
return strcmp(*pa, *pb);
}
int compare_any(void * arr, size_t size, int (*cfunc)(void *, void *)) {
char * first = arr;
char * second = first + size;
return cfunc(first, second);
}
int main(void) {
int n[2] = {1, 2};
if ( compare_any(n, sizeof(*n), compare_int) ) {
puts("a and b are not equal");
} else {
puts("a and b are equal");
}
double d[2] = {3.0, 3.0};
if ( compare_any(d, sizeof(*d), compare_double) ) {
puts("c and d are not equal");
} else {
puts("c and d are equal");
}
char * s[] = {"abcd", "bcde"};
if ( compare_any(s, sizeof(*s), compare_string) ) {
puts("'abcd' and 'bcde' are not equal");
} else {
puts("'abcd' and 'bcde' are equal");
}
return 0;
}
Outputs:
paul#local:~/src/c/scratch$ ./comp
a and b are not equal
c and d are equal
'abcd' and 'bcde' are not equal
paul#local:~/src/c/scratch$
As you can see, there's no way compare_any() could accept both an array of int, and an array of char *, without the compare_string() function getting a pointer it needs to treat as a char **, because of the pointer arithmetic it performs on the array elements. Without that additional level of indirection, neither compare_int() nor compare_double() could function.
I'm currently attempting to use the built-in quicksort provided by C in order to sort an array of pointers to structs. I want to sort each element based on a name element within the struct.
Although my debug output of the entire array each time through the comparison function shows me that the function is indeed shifting elements, the end result is not the correct sorted order. Is there something I'm just not seeing here?
typedef struct // The custom data type.
{
char *name;
} Person;
----------------------------
Person **people; // A dynamically allocated array of Person pointers.
int numPeople; // The logical index of people.
int maxPeople; // The current maximum capacity of people.
int compare(const void *a, const void *b) // The comparison function for determining
{ // alphabetic ordering.
const Person *const *p1 = a;
const Person *const *p2 = b;
return strcmp((*p1)->name, (*p2)->name); // Compare alphabetically, return result.
}
void SomeFunction(void)
{
qsort(people, numPeople, sizeof(Person *), compare); // Perform the sort.
}
Thanks for help with this.
I have tested your code and it looks working OK. Here is the code I compiled with gcc 4.5.2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct // The custom data type.
{
char *name;
} Person;
Person **people; // A dynamically allocated array of Person pointers.
int numPeople; // The logical index of people.
int maxPeople; // The current maximum capacity of people.
int compare(const void *a, const void *b) // The comparison function for determining
{ // alphabetic ordering.
const Person *const *p1 = a;
const Person *const *p2 = b;
return strcmp((*p1)->name, (*p2)->name); // Compare alphabetically, return result.
}
void SomeFunction(void)
{
qsort(people, numPeople, sizeof(Person *), compare); // Perform the sort.
}
int main()
{
int iCnt;
maxPeople = 4;
numPeople = 4;
people = calloc(1, sizeof(Person *) * maxPeople);
people[0] = calloc(1, sizeof(Person));
people[1] = calloc(1, sizeof(Person));
people[2] = calloc(1, sizeof(Person));
people[3] = calloc(1, sizeof(Person));
people[0]->name = strdup("Tanya");
people[1]->name = strdup("Alfred");
people[2]->name = strdup("Harry");
people[3]->name = strdup("Oakley");
for(iCnt = 0; iCnt < numPeople; iCnt ++)
printf("[%d] %s\n", iCnt, people[iCnt]->name);
SomeFunction();
for(iCnt = 0; iCnt < numPeople; iCnt ++)
printf("[%d] %s\n", iCnt, people[iCnt]->name);
return 0;
}
The code looks legit and I'm not sure what's wrong. Could you try compiling the code I tested and see if it works?
Can you try with this
int compare(const void *a, const void *b) // The comparison function for determining
{ // alphabetic ordering.
const Person *p1 = *(const Person**)a;
const Person *p2 = *(const Person**)b;
return strcmp((p1)->name, (p2)->name); // Compare alphabetically, return result.
}
There are lots of questions on stackoverflow regarding how to sort an array of structure pointers. I looked through them all, to no avail. I want to sort an array of pointers to an array of structures. I first allocate storage for the pointer array, then for the structures themselves. All that seems fine, but I can't get them sorted. I'm sure the problem is in the compare function. I've copied a few of them from stackoverflow, and they are listed below. But none of them work...
typedef struct s_stream{
int amc;
char *name;
} dataStream;
void abc(void)
{
int count = 100;
dataStream *_UniqueStreamBuild = calloc(count, sizeof(dataStream ));
dataStream **UniqueStreamBuild = calloc(count, sizeof(dataStream *));
for ( int i = 0; i < count; ++i) UniqueStreamBuild[i] = _UniqueStreamBuild + i;
//**Edit: ******** **
// here I call a cascade of functions that assign values to amc; those
// functions are correct: they produce an unsorted array of amc values;
// the output I am getting is an array of structures seemingly in random order.
qsort(UniqueStreamBuild, count, sizeof(dataStream *), compare);
}
int compare (const void * a, const void * b)
{
const dataStream *x = a;
const dataStream *y = b;
if (x->amc > x->amc)
return(1);
if (x->amc < x->amc)
return(-1);
return(0);
}
int compare( const void *a, const void *b )
{
dataStream *m1 = *(dataStream **)a;
dataStream *m2 = *(dataStream **)b;
if (m1->amc > m2->amc)
return(1);
if (m1->amc < m2->amc)
return(-1);
return(0);
}
Your second possible compare() function should work unless there's some difference I didn't notice between it and this version just below. When sorting an array of pointers, the comparison function is passed two pointers to a dataStream *, hence the comparator should be very similar to this:
int compare (const void *a, const void *b)
{
const dataStream *x = *(const dataStream **)a;
const dataStream *y = *(const dataStream **)b;
if (x->amc > y->amc)
return(1);
else if (x->amc < y->amc)
return(-1);
else
return(0);
}
Also, as originally written, one of your functions always returns 0 because x->amc == x->amc (you dereference x twice, instead of x and y).
Your test code does not fully initialize the data structures - it uses calloc() so the strings and pointers in the structures are all zeroed, so sorting doesn't do much.
This code works for me...how about you?
#include <stdio.h>
#include <stdlib.h>
typedef struct s_stream
{
int amc;
char *name;
} dataStream;
static int compare(const void *a, const void *b)
{
const dataStream *x = *(const dataStream **)a;
const dataStream *y = *(const dataStream **)b;
if (x->amc > y->amc)
return(1);
else if (x->amc < y->amc)
return(-1);
else
return(0);
}
static void dump(FILE *fp, const char *tag, dataStream * const * const data, int num)
{
const char *pad = "";
fprintf(fp, "Stream Dump (%s): (%d items)\n", tag, num);
for (int i = 0; i < num; i++)
{
fprintf(fp, "%s%d", pad, data[i]->amc);
if (i % 10 == 9)
{
putc('\n', fp);
pad = "";
}
else
pad = ", ";
}
putc('\n', fp);
}
static void abc(void)
{
int count = 100;
dataStream *_UniqueStreamBuild = calloc(count, sizeof(dataStream ));
dataStream **UniqueStreamBuild = calloc(count, sizeof(dataStream *));
for ( int i = 0; i < count; ++i)
{
UniqueStreamBuild[i] = _UniqueStreamBuild + i;
UniqueStreamBuild[i]->amc = (7 * i + 3) % count + 1;
}
dump(stdout, "Before", UniqueStreamBuild, count);
qsort(UniqueStreamBuild, count, sizeof(dataStream *), compare);
dump(stdout, "After", UniqueStreamBuild, count);
free(_UniqueStreamBuild);
free(UniqueStreamBuild);
}
int main(void)
{
abc();
return 0;
}
I messed around with this enough but I really don't get it.
Here is what I want to do: Take a 2D char array as an input in a function, change the values in it and then return another 2D char array.
That's it. Quite simple idea, but ideas do not get to work easily in C.
Any idea to get me started in its simplest form is appreciated. Thanks.
C will not return an array from a function.
You can do several things that might be close enough:
You can package your array in struct and return that. C will return structs from functions just fine. The downside is this can be a lot of memory copying back and forth:
struct arr {
int arr[50][50];
}
struct arr function(struct arr a) {
struct arr result;
/* operate on a.arr[i][j]
storing into result.arr[i][j] */
return result;
}
You can return a pointer to your array. This pointer must point to memory you allocate with malloc(3) for the array. (Or another memory allocation primitive that doesn't allocate memory from the stack.)
int **function(int param[][50]) {
int arr[][50] = malloc(50 * 50 * sizeof int);
/* store into arr[i][j] */
return arr;
}
You can operate on the array pointer passed into your function and modify the input array in place.
void function(int param[][50]) {
/* operate on param[i][j] directly -- destroys input */
}
You can use a parameter as an "output variable" and use that to "return" the new array. This is best if you want the caller to allocate memory or if you want to indicate success or failure:
int output[][50];
int function(int param[][50], int &output[][50]) {
output = malloc(50 * 50 * sizeof int);
/* write into output[i][j] */
return success_or_failure;
}
Or, for the caller to allocate:
int output[50][50];
void function(int param[][50], int output[][50]) {
/* write into output[i][j] */
}
You cannot return an array from a function.
You have several options:
wrap arrays inside structs
struct wraparray {
int array[42][42];
};
struct wraparray foobar(void) {
struct wraparray ret = {0};
return ret;
}
pass the destination array, as a pointer to its first element (and its size), to the function; and change that array
int foobar(int *dst, size_t rows, size_t cols, const int *src) {
size_t len = rows * cols;
while (len--) {
*dst++ = 42 + *src++;
}
return 0; /* ok */
}
// example usage
int x[42][42];
int y[42][42];
foobar(x[0], 42, 42, y[0]);
change the original array
int foobar(int *arr, size_t rows, size_t cols) {
size_t len = rows * cols;
while (len--) *arr++ = 0;
return 0; /* ok */
}
char **foo(const char * const * bar, size_t const *bar_len, size_t len0) {
size_t i;
char** arr = malloc(sizeof(char *) * len0);
for (i = 0; i < len0; ++i) {
arr[i] = malloc(bar_len[i]);
memcpy(arr[i], bar[i], bar_len[i]);
}
/* do something with arr */
return arr;
}
Somewhere else in your code:
char **pp;
size_t *pl;
size_t ppl;
/* Assume pp, pl are valid */
char **pq = foo(pp, pl, ppl);
/* Do something with pq */
/* ... */
/* Cleanup pq */
{
size_t i;
for (i = 0; i < ppl; ++i)
free(pq[i]);
free(pq);
}
Because you're passing by-pointer instead of by-value and you want to write to the input array, you have to make a copy of it.
Here's another example. Tested and works.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void test(char**,unsigned int,unsigned int);
const unsigned int sz_fld = 50 + 1;
const unsigned int sz_ffld = 10;
int main(void) {
char fld[sz_ffld][sz_fld];
for (unsigned char i=0;i<sz_ffld;++i) {
strcpy(fld[i],"");
}
strcpy(fld[0],"one");
strcpy(fld[1],"two");
strcpy(fld[2],"three");
char** pfld = malloc(sz_ffld*sizeof(char*));
for (unsigned int i=0;i<sz_ffld;++i) {
*(pfld+i) = &fld[i][0];
}
test(pfld,sz_ffld,sz_fld);
printf("%s\n",fld[0]);
printf("%s\n",fld[1]);
printf("%s\n",fld[2]);
free(pfld);
return(0);
}
void test(char** fld,unsigned int m,unsigned int n) {
strcpy(*(fld+0),"eleven");
strcpy(*(fld+1),"twelve");
return;
}
Note the following:
For compiling, I am using gcc with the C99 option.
I defined the function to include the two sizes information, but I wrote very basic code and am not actually using the information at all, just the strcpy(), so this certainly is not security-safe code in any way (even though I'm showing the "m" and "n" for such facility). It merely shows a technique for making a static 2D char array, and working with it in a function through the intermediate of an array of pointers to the "strings" of the array.
When you pass a 2D array to a function as a parameter, you need to explicitly tell it the size of the arrays second dimension
void MyFunction(array2d[][20]) { ... }
The following will do what you want. it will print "One" and "Ten". Also note that it is typed to the exact array dimensions of 10 and 8.
char my_array[10][8] =
{
{"One"},
{"Two"},
{"One"},
{"One"},
{"One"},
{"One"},
{"One"},
{"One"},
{"Nine"},
{"Ten"},
};
void foo ( char (**ret)[10][8] )
{
*ret = my_array;
}
void main()
{
char (*ret)[10][8];
foo(&ret);
printf("%s\r\n", (*ret)[0] )
printf("%s\r\n", (*ret)[9] )
}
The original question was about RETURNING the array, so I'm updating this to show returning a value. You can't "return an array" directly, but you CAN make a typedef of an array and return that...
char my_array[10][8];
typedef char ReturnArray[8];
ReturnArray* foo()
{
return my_array;
}