qsort an array of strings, compare - c

I am trying to figure out how to use qsort with an array of strings. My code looks like this.
char words[500][256];
int numOfWords; // this is calculated above
int sortWordList() {
int length = sizeof(words) / sizeof(char *);
qsort(words, length, sizeof(char*), compare);
}
int compare (const void * a, const void * b ) {
const char *pa = *(const char**)a;
const char *pb = *(const char**)b;
return strcmp(pa,pb);
}
However, I get a "Access violation reading location 0x###.." everytime and I dont know whats wrong. Can anyone spot my problem?
EDIT: Thanks for the wonderful help. You guys are always the best.

You are not casting your const void * to const char * properly, to do so, use instead:
const char *pa = (const char *)a;
const char *pb = (const char *)b;
Plus compare() should be above sortWordList() as you're using it in sortWordList().

Related

getting at pointers in an array of struct passed as a void*

I have a function with a signature like qsort:
const char* get_str(int i, const void *base, size_t nmemb, size_t size);
I am passed arrays of pointers to const char*, or arrays of structs whose first field is a pointer to const char*.
What casts do I need to do to extract that pointer in the array element i?
I have tried casting the base as an array of char itself, so I can advance to the right element:
return *(const char**)(((const char*)base)[i * size]);
But the compiler is complaining: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
It looks like you want to implement a type or identification sytem for structs like so:
#include <stdlib.h>
#include <stdio.h>
struct a {
const char *id;
int x;
};
struct b {
const char *id;
double d;
};
union any {
struct a a;
struct b b;
};
int main()
{
struct a a[] = {{"one", 1}, {"two", 2}, {"three", 3}};
struct b b[] = {{"pi", 3.1415}, {"e", 2.71}};
union any any[3];
any[0].a = a[0];
any[1].b = b[0];
any[2].a = a[1];
puts(get_str(1, a, 3, sizeof(*a)));
puts(get_str(1, b, 2, sizeof(*b)));
puts(get_str(1, any, 3, sizeof(*any)));
return 0;
}
In this case, the following works:
const char* get_str(int i, const void *base, size_t nmemb, size_t size)
{
const char *p = base;
const char **pp = (const char**) (p + i * size);
return *pp;
}
This can be written in one line as:
const char* get_str(int i, const void *base, size_t nmemb, size_t size)
{
return *(const char**) ((const char *) base + i * size);
}
But I think that the detour via void pointers is not necessary. You can do the address calculations with the typed array:
puts(*(const char **) (&a[1]));
puts(*(const char **) (&b[1]));
puts(*(const char **) (&any[1]));
If you wrap that in a function:
const char *get_str(const void *str)
{
return *(const char **) str;
}
you get:
puts(get_str(&a[1]));
puts(get_str(&b[1]));
puts(get_str(any + 1));
which is more readable than the qsortish syntax in my opinion.
This works, because you acces only one element at a known position. The functions bsort and qsort, however, can't use this technique, because they have to access the array at several indices and hence must be able to do the index calculation themselves.
Find me (well, "yet another" but suppose this parentheses never existed!) a bug in that compiler and I give you a free cookie!
The compiler is right. This
*(const char**)(((const char*)base)[i * size]);
Will type-compile to something like (syntax: expresion -> type)...
base -> const void*
(const char*)base -> (const char*)const void*
(((const char*)base)[i * size]) -> *(const char*)const void* -> const char // Here's the problem!
(const char**)(((const char*)base)[i * size]) -> (const char**)const char // Now, this is *not* good...
*(const char**)(((const char*)base)[i * size]) -> *(const char**)const char -> const char* // Well, we have now just perfectly dereferenced '~'...
Not very sane, isn't it?*
BTW: You don't give us enough information for me to provide a full solution to your problem. What are those structs you talk about?
Edit: May this help you (written according to comments)?
*(const char**)&(((const unsigned char*)base)[size * i])

Pointer Initialization to Iterate through Array

I have a function, where I have 2 void pointers (part of the specification), but I know they are char *. I want to iterate through the char arrays, so I tried to create some pointers to iterate through them. When I do the following, my program doesn't work:
int foo(void const * first, void const * second)
{
char const * firstIt = (const char*) first;
char const * secondIt = (const char*) second;
...
}
However, if I do:
int foo(void const * first, void const * second)
{
char const * firstIt = *(const char**) first;
char const * secondIt = *(const char**) second;
...
}
What is the difference between the two and why does the second one work? I don't know if I included enough detail, so if more information is needed I'd be happy to provide it.
If the second one works, this is because the void pointer that you indicate for the function can really be anything, and I am guessing that you are passing to the function the pointer of a pointer. For example, the following code works:
#include <stdio.h>
#include <stdlib.h>
int foo(void const * first, void const * second);
int goo(void const * first, void const * second);
int main () {
char * a, * b;
a = malloc (sizeof (char));
b = malloc (sizeof (char));
*a = 'z';
*b = 'x';
goo (&a, &b); /* critical line */
free (a);
free (b);
return 0;
}
int foo(void const * first, void const * second) {
char const * firstIt = (const char*) first;
char const * secondIt = (const char*) second;
printf ("%c %c", *firstIt, *secondIt);
return 1;
}
int goo(void const * first, void const * second) {
char const * firstIt = *(const char**) first;
char const * secondIt = *(const char**) second;
printf ("%c %c", *firstIt, *secondIt);
return 2;
}
However, for the above program to work with the function foo you need to replace the critical line with a call of the form:
foo (a, b);
Does the difference make sense? Did it solve your problem?
The first approach assumes the caller has passed a char * (const qualified in some way).
The second assumes the caller has passed a char ** (const qualified in some way).
If the second one works, that means your caller is passing a char **.
The reason the first wouldn't work is undefined behaviour. Having a pointer of one type, converting to another type, and dereferencing it as anything other than the original type gives undefined behaviour. A round trip via a void pointer doesn't change that.
That is why compilers complain about implicit conversions of one pointer type to another (except to and from void pointers).

Sorting a 2D char array but by number

I have been searching for a solution but can't seem to find one similar to mine. Am trying to sort
a 2D char * array by a certain column.
char *objs[50][3];
/***** within a loop to populate with values *****/
objs[count][0]=obj->level; //this is a number to be sorted
objs[count][1]=obj->cost; //this is a number
objs[count][2]=obj->short_desc->str; //this is a string
count++;
/***** end loop *********/
qsort(objs, count, sizeof(char *), compare_function); //to sort by obj->level, int values
i deleted my previous solution because it was showing all kinds
of weird numbers or not even sorting. i am not very experienced with
C, and would greatly appreciate help on how to do this.
thank you in advance.
When sorting implicit structs in "C" defined by 2d arrays, where each row corresponds to a single object, I find it useful to use qsort_s() because it allows me to pass in more information about the the array entries to be sorted into the comparison function.
Thus the following accepts a 2d array of strings with nRows rows and nColumns columns and sorts on a column specified by sortColumnIndex. The extra context pointer provided to qsort_s() communicates the sort index down to the sorting method without requiring global variables:
struct sort_on_index_context
{
size_t nRows;
size_t nColumns;
size_t sortColumnIndex;
};
static int compare(void *p_vcontext, const void *ventry1, const void *ventry2)
{
struct sort_on_index_context *p_context = (struct sort_on_index_context *)p_vcontext;
char **entry1 = (char **)ventry1;
char *s1 = entry1[p_context->sortColumnIndex];
char **entry2 = (char **)ventry2;
char *s2 = entry2[p_context->sortColumnIndex];
return strcmp(s1, s2);
}
void sort_on_index(char **objs, size_t nRows, size_t nColumns, size_t sortColumnIndex)
{
struct sort_on_index_context context;
if (sortColumnIndex < 0 || sortColumnIndex >= nColumns)
return; /* Print an error or throw an exception! */
context.nColumns = nColumns;
context.sortColumnIndex = sortColumnIndex;
context.nRows = nRows;
qsort_s(objs, nRows, sizeof(char *) * nColumns, compare, (void *)&context);
}
We pass sizeof(char *) * nColumns because we want qsort_s to treat each contiguous group of nColumns char pointers as single blocks to be rearranged in order.
And then you would call it something like this:
char *objs[50][3];
size_t nRows = sizeof(objs)/sizeof(objs[0]);
size_t nColumns = sizeof(objs[0])/sizeof(objs[0][0]);
size_t column_id_to_sort = 1; /* or whatever you want to define as your sort key. */
/* Fill up your "objs" array however you like */
/* Now do the sort: */
sort_on_index(&objs[0][0], nRows, nColumns, column_id_to_sort);
Edit
If qsort_s or some equivalent is not available in your development environment, you may need to communicate the necessary information to your sort function via a static variable, e.g.
static struct sort_on_index_context context;
static int compare(const void *ventry1, const void *ventry2)
{
char **entry1 = (char **)ventry1;
char *s1 = entry1[context.sortColumnIndex];
char **entry2 = (char **)ventry2;
char *s2 = entry1[context.sortColumnIndex];
return strcmp(s1, s2);
}
Update
You can extend the method to supply your own custom comparison method like so:
struct sort_on_index_context_custom
{
size_t nRows;
size_t nColumns;
size_t sortColumnIndex;
int (*comparer)(const char *, const char *);
};
static int compare_custom(void *p_vcontext, const void *ventry1, const void *ventry2)
{
struct sort_on_index_context_custom *p_context = (struct sort_on_index_context_custom *)p_vcontext;
char **entry1 = (char **)ventry1;
char *s1 = entry1[p_context->sortColumnIndex];
char **entry2 = (char **)ventry2;
char *s2 = entry2[p_context->sortColumnIndex];
return p_context->comparer(s1, s2);
}
void sort_on_index_custom(char **objs, size_t nRows, size_t nColumns, size_t sortColumnIndex, int (*comparer)(const char *, const char *))
{
struct sort_on_index_context_custom context;
if (sortColumnIndex < 0 || sortColumnIndex >= nColumns)
return; /* Print an error or throw an exception! */
context.nColumns = nColumns;
context.sortColumnIndex = sortColumnIndex;
context.nRows = nRows;
context.comparer = comparer;
qsort_s(objs, nRows, sizeof(char *) * nColumns, compare_custom, (void *)&context);
}
And then call it like this to sort a given column of strings as integers:
static int integer_compare(const char *s1, const char *s2)
{
int int1 = atoi(s1);
int int2 = atoi(s2);
return int1 - int2;
}
sort_on_index_custom(&objs[0][0], nRows, nColumns, 1, integer_compare);

Sort char pointer array in C

I am trying to sort array of char pointers, for that purpose I use qsort function, but I can't understand what I am doing wrong and how I can sort that array.
int StringCompare( const void* a, const void* b)
{
char const *char_a = (char const *)a;
char const *char_b = (char const *)b;
return strcmp(char_a, char_b);
}
int main() {
char *a[] = { "Garima",
"Amit",
"Gaurav",
"Vaibhav"
};
int n;
qsort( a, 4, sizeof(char*), StringCompare);
for (n=0; n<4; n++)
printf ("%c ",*a[n]);
}
The Output is: G A G V
The issue is that the values passed to the sort function (a.k.a StringCompare) are pointers into the a array. In other words, they are of type const char **.
You need to instead declare char_a and char_b as const char **, and dereference them in the call to strcmp:
int StringCompare( const void* a, const void* b)
{
char const **char_a = a;
char const **char_b = b;
return strcmp(*char_a, *char_b);
}
Also note the casts are unnecessary.
proper comparator:
int StringCompare( const void* a, const void* b)
{
char const *char_a = *(char const **)a;
char const *char_b = *(char const **)b;
return strcmp(char_a, char_b);
}
NOTE:
according to sort description comparator function is:
compar
Pointer to a function that compares two elements.
This function is called repeatedly by qsort to compare two elements.
It shall follow the following prototype:
int compar (const void* p1, const void* p2);
so, it receives not a char*, but char**
proper output cycle:
for (n=0; n<4; n++)
printf ("%s ", a[n]);
Define your StringCompare function this way:
int StringCompare(const char **a, const char **b)
{
return strcmp(*a, *b);
}
No need to clutter the code with explicit casting because you can implicitly cast a void pointer to any other pointer type.
If you want to sort char-arrays for the first letters, you could implement a function, that looks at the (unsigned) values of the first char in the array. As they are all equal to the numbers in the ASCII-standards. But you have to be careful if you mix upper case chars with lower case chars.
I know... its not a some special implemented function, but I once programmed it that way and it worked.

Convert Char *ptr to Char *ptrArray[] to use in qsort

Working on using qsort.
#include <string.h>
#include <stdlib.h>
#pragma once
int cstring_cmp(const void *a, const void *b);
int main()
{
int count = 0;
char * randomStr = "sdjsn9i3ms;sa;'smsn92;w;''[w0p4;dsmsdf";
char * charArray[] =
{"s","d","j","s","n","9","i","3","m","s",";","s","a",";","'","s","m","s","n"
,"9","2",";","w",";","'","'","[","w","0","p","4",";","d","s","m","s","d","f"};
size_t strings_len = sizeof(charArray) / sizeof(char *);
/*void qsort(void *base, size_t nel,
size_t width, int (*compar)(const void *, const void *));*/
qsort(charArray, strings_len, sizeof(char *), cstring_cmp);
qsort(randomStr, strings_len, sizeof(char *), cstring_cmp);
// Pause at command prompt
system("pause");
return 0;
} // Close function Main
int cstring_cmp(const void *a, const void *b)
{
const char **ia = (const char **)a;
const char **ib = (const char **)b;
return strcmp(*ia, *ib);
}
So obviously my second qsort is not working. whether that's based on my cstring_cmp function that goes into my qsort not being able to support the base i give it or because my base is not formatted to correctly input into the qsort is a mystery to me.
My Question is how do I convert char * randomStr to char * charArray[] dynamically, during runtime, on the fly, or whatever cool phrase you can come up with. I've searched around a lot and maybe I was just not asking the right question, so I'm coming to you guys for some real question answering power.
Just starting C so if you please try not to fry my brain with your answers me and my brain would appreciate it.
My end goal here is to convert randomStr to a format of charArray, qsort it then convert it back to the randomStr format so i can do some find and replace things I have set up already.
Any help would be great, thanks.
First of all if you use char *randomStr = "Stuff" you can't change it, it's undefined behavior. Second, try this:
int
cmp_fry_brain(const void *a, const void *b)
{
return *((const char *)a) - *((const char *)b);
}
/* This is equivalent to the one above (the compiler will likely emit the
* exact same code).
*/
int
cmp(const void *a, const void *b)
{
const char *x = a;
const char *y = b;
return *x - *y;
}
int
main()
{
char str[] = "This is the end";
qsort(str, strlen(str), 1, cmp_fry_brain);
/* ... */
}

Resources