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])
Related
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).
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);
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().
When I'm learning to use qsort to sort an array of string, there is a question puzzled me.
For example, to sort the following s
char *s[] = {
"Amit",
"Garima",
"Gaurav",
"Vaibhav"
};
To use the qsort, you must provide a comparison function like the
following function cstring_cmp I guess in the qsort function, the type of parameter to be passed to the function cstring_cmp is char**. How to convert a char** to a void*? Why can we convert a char** to a void*?
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);
/* return the negative of the normal comparison */
}
Your question seems a bit vague but I'll give it a go anyway. To answer how, you can convert any pointer type to any other pointer type in C by simply casting. To answer why, well that's how C is defined.
The qsort() function requires a function with the given prototype (with const void *) parameters. This is because qsort() is unaware of the actual data type you are sorting, and must use a consistent function prototype for the comparison callback. Your comparison callback is responsible for converting the const void * parameters to pointers to the actual types in your array, in your case const char **.
The example you provide is being setup to ask qsort() to sort an array of char pointers (char *). This comparator you're providing is given each 'pair' of items the algorithm needs, by address. two char pointers. the address qsort() uses is based on the root address you give it, adding size-bytes per "item". Since each "item" is a char*, the size of each item is, in fact, the size of a pointer.
I've modified the comparator to demonstrate what is being compared, and what the addresses are that are being passed in. you will see they are all increments off the base address of the array containing all the char *s.
char *mystrings[] =
{
"This",
"is",
"a",
"test",
"of",
"pointers",
"to",
"strings"
};
int cstring_cmp(const void *a, const void *b)
{
const char **ia = (const char **)a;
const char **ib = (const char **)b;
printf("%p:%s - %p:%s\n", a, *ia, b, *ib);
return -strcasecmp(*ia, *ib);
}
int main(int argc, char *argv[])
{
printf("Base address of our pointer array: %p\n\n", mystrings);
qsort(mystrings, sizeof(mystrings)/sizeof(mystrings[0]), sizeof(char*), cstring_cmp);
for (size_t i=0; i<sizeof(mystrings)/sizeof(mystrings[0]);i++)
printf("%s\n", mystrings[i]);
return 0;
}
produces the following output:
Base address of our pointer array: 0x100006240
0x100006240:This - 0x100006260:of
0x100006260:of - 0x100006278:strings
0x100006240:This - 0x100006278:strings
0x100006248:is - 0x100006240:strings
0x100006278:This - 0x100006240:strings
0x100006250:a - 0x100006240:strings
0x100006270:to - 0x100006240:strings
0x100006258:test - 0x100006240:strings
0x100006260:of - 0x100006240:strings
0x100006268:pointers - 0x100006240:strings
0x100006260:of - 0x100006240:strings
0x100006240:test - 0x100006248:This
0x100006248:test - 0x100006250:to
0x100006240:This - 0x100006248:to
0x100006260:of - 0x100006268:pointers
0x100006268:of - 0x100006270:a
0x100006270:a - 0x100006278:is
0x100006268:of - 0x100006270:is
to
This
test
strings
pointers
of
is
a
A even less visualized one:
int cstring_cmp(const void *a, const void *b){
return -strcasecmp((char *)(*((char **)a)), (char *)(*((char **)b)));
}
But you can see , a and b are char **, and they are dereferenced and become char * and passed to strcasecmp.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int cstring_cmp(const void *a, const void *b){
return -strcasecmp((char *)(*((char **)a)), (char *)(*((char **)b)));
}
int main(){
char *s[] = { "Amit",
"Garima",
"Vaibhav",
"Gaurav"};
qsort(s, 4, sizeof(char *), cstring_cmp);
printf("%s\n%s\n%s\n%s\n", s[0], s[1], s[2], s[3]);
return 0;
}
Output:
Vaibhav
Gaurav
Garima
Amit
It is legal to cast any pointer to char * or void * because void * means a pointer to a memory (RAM or virtual) byte.
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);
/* ... */
}