Inconsistency in size of array cells - c

i'm doing a school project implementing some sorting algorithms in C codes, and i'm working on a Binary Insertion Sort on some generic data type arrays (so i'm using void* items and void** arrays).
I have a binarySearch function that returns the index i would have to insert an item into the array to preserve the ordering of its elements (according to a given function that i pass to the sorting function), and this works correctly.
int binary_search(void **arr, void *item, long start, long end, int data_size, compFunc compare)
{
int s = start, e = end;
while (s <= e)
{
long middle = s + (e - s) / 2;
int comparison = compare(item, arr[middle]);
if (comparison == 0)
return middle;
else if (comparison > 0)
s = middle + 1;
else
e = middle - 1;
}
return s;
}
Then i have the binaryInsertSort function
void binary_insert_sort(void **arr, long arr_size, int data_size, compFunc compare)
{
long explored, j, pos;
void *current = malloc(sizeof(void*)), *holder;
if(!current){
perror("Error allocating memory\n");
exit(EXIT_FAILURE);
}
for (explored = 1; explored < arr_size; explored++)
{
memcpy(current, arr[explored], data_size);
j = explored - 1;
pos = binary_search(arr, current, 0, j, data_size, compare);
while (j >= pos)
{
holder = malloc(sizeof(void *));
memcpy(holder, (*arr) + (j + 1)*data_size, data_size);
//CRASHES HERE
memcpy((*arr) + (j + 1) * data_size, (*arr) + (j * data_size), data_size);
memcpy((*arr) + (j * data_size), holder, data_size);
j--;
free(holder);
}
}
free(current);
}
I call these functions like this
int main(int argc, char const *argv[])
{
char* arr[] = {"a", "b", "f", "d", "c", "g", "e", "1"};
int n = sizeof(arr)/sizeof(char*);
binary_insert_sort((void**)arr, n, sizeof(char*), string_compare);
print_string_array(arr, n);
return 0;
}
But it always crashes after the first memcpy , when i try to move arr[j+1] into arr[j] ; i tried doing some debugging and, after trying to print (int)(arr[1]-arr[0])(which should print the size of a cell if i understand correctly), i noticed cells have size = 2 rather than the expected sizeof(char*)=4 , so there are problem accessing them correctly when using *arr + j*data_size , since i'm moving j * 2 cells rather than j
why does this happen?
i apologize if i'm missing something basic, or if english or formatting arent right, 1st time asking

i'm doing a school project implementing some sorting algorithms in C codes, and i'm working on a Binary Insertion Sort on some generic data type arrays (so i'm using void* items and void** arrays).
You clarified in comments that what you mean is that you want a function that can sort arrays having any element type. This is exactly what the standard library's qsort() function does, so you should look to it for guidance on how such a function might look and work.
In particular, you need to understand that C has no generic data type. In particular, a void * can point to an object of any type, but void * itself is a specific, complete type, not generic in any way.* Thus, using void * items does not serve your purpose at all. Not even if you wanted to sort only arrays of pointers, because the C language does not guarantee that different pointer types have the same representation as each other, or even the same size, except only that char * and void * are required to have the same size and representation.
In other words, no, you don't have void * items, and you don't want to sort a "void ** array". And therefore no, your binarySearch() function for an array of void * does not serve your purposes -- however well it does its job, it's the wrong job.
Following qsort(), here's a signature that would serve your purpose:
typedef int (*compFunc)(const void *, const void *);
void binary_insert_sort(void *arr, size_t element_count, size_t element_size,
compFunc compare);
Note that the array to sort is conveyed via a pointer to its first element, received by the function as a pointer of type void * -- not void **. This does present an issue, however: you cannot perform pointer arithmetic or array indexing on a void *, because these operations are defined in terms of the size of the pointed-to type, which is unknown in this case because void is an incomplete type. But it should not be a particular surprise that such an issue arises, because the whole point of the exercise is to sort objects whose size is not known when the the function is compiled.
So what do you do? The traditional approach would be via converting to char *:
#define ELEMENT_POINTER(base, index, size) ((char *) (base) + (index) * (size))
void *element_3_for_example = ELEMENT_POINTER(arr, 3, element_size);
So a comparison would then look like this ...
int result = compare(ELEMENT_POINTER(arr, i, element_size),
ELEMENT_POINTER(arr, j, element_size));
... and a swap might look like this:
void *temp = malloc(element_size);
// ...
memcpy(temp, ELEMENT_POINTER(arr, i, element_size));
memcpy(ELEMENT_POINTER(arr, i, element_size), ELEMENT_POINTER(arr, j, element_size));
memcpy(ELEMENT_POINTER(arr, j, element_size), temp);
// ...
free(temp);
I'll leave it to you to work the actual exercise in terms of those or similar constructs.
As for the actual question ...
why does this happen?
, it's because your function is confused about whether the elements of the array are themselves the items being sorted or whether they are pointers to the elements being sorted. Erroneously assuming the latter, it makes the further questionable choice of swapping the data instead of the pointers. In this particular case, the data happen to be pointers after all, though that would not always be the case. The data they point to are arrays containing string literals, and
These arrays are not the expected size, so you have bounds overruns on both reading and writing, and
They are not writable anyway, which is probably the specific source of the error.
* One could consider void to be a generic data type, as indeed this answer could be taken to demonstrate. But you cannot declare an object to have type void, nor access an object via an lvalue of type void, so this is largely moot.

Your approach is initially wrong due the function declarations and their calls like for example
binary_insert_sort((void**)arr, n, sizeof(char*), string_compare);
^^^^^^^^^^^
That is if within the function you will dereference the pointer like for example
arr[explored]
then the expression will have the type void *. So if the original array has for example the type
char arr[] = "hello";
then in the expression above there will be used incorrect pointer arithmetic. That is instead of evaluation the value of the pointer expression like value of arr + explored * sizeof( char ) the value of the pointer expression will be evaluated like value of arr + explored * sizeof( void * ).
Also the function is inefficient. There are too many memory allocations in the while loop
while (j >= pos)
{
holder = malloc(sizeof(void *));
//...
The function should be declared at least like
void binary_insert_sort( void *arr, size_t arr_size, size_t data_size, compFunc compare);
The function binary_search has a redundant parameter start. You are calling the function always passing 0 as its argument for the parameter start
pos = binary_search(arr, current, 0, j, data_size, compare);
Instead of the parameters start and end it is enough to pass the number of elements in the sub-array. So the function could be declared like
int binary_search( const void *arr, const void *item, size_t arr_size, size_t data_size, compFunc compare);
Or similarly to the standard C function bsearch like
int binary_search( const void *item, const void *arr, size_t arr_size, size_t data_size, compFunc compare);
If the sub-array already has the element that is equal to the searched element then the function should return the position after the existent element in the sub-array instead of returning the position of the existent element.

Ok so thanks to the help of the previous comments i managed to get it somewhat going, here is the updated code
#define GET_PTR(base, offset, size) ((char *)(base) + (offset) * (size))
long binary_search(void *arr, void *item, size_t end, int data_size, compFunc compare)
{
long s = 0, e = end, middle;
int comparison;
while (s <= e)
{
middle = s + (e - s) / 2;
comparison = compare(item, GET_PTR(arr, middle, data_size));
if (comparison == 0)
return middle;
else if (comparison > 0)
s = middle + 1;
else
e = middle - 1;
}
return s;
}
void swap(void *base, long ind1, long ind2, size_t data_size)
{
void *temp = malloc(data_size);
memcpy(temp, GET_PTR(base, ind2, data_size), data_size);
memcpy(GET_PTR(base, ind2, data_size), GET_PTR(base, ind1, data_size), data_size);
memcpy(GET_PTR(base, ind1, data_size), temp, data_size);
free(temp);
}
void binary_insert_sort(void *arr, size_t arr_size, size_t data_size, compFunc cmp)
{
void *current = malloc(data_size), *holder;
long explored, shifting, bs_pos;
for (explored = 1; explored < arr_size; explored++)
{
current = GET_PTR(arr, explored, data_size);
shifting = explored - 1;
bs_pos = binary_search(arr, current, shifting, data_size, cmp);
while (shifting >= bs_pos)
{
swap(arr, shifting, shifting + 1, data_size);
shifting--;
}
}
free(current);
}
and this seems to work, it manages to correctly sort an array of int and of a 'Person' struct when called like this
int main(int argc, char const *argv[])
{
// char *string_arr[] = {"a", "b", "f", "d", "c", "g", "e", "1"};
// int char_size = sizeof(arr) / sizeof(arr[0]);
// print_string_array((char**)arr, char_size);
// binary_insert_sort((void*)arr, char_size, sizeof(char*), string_compare);
// print_string_array((char**)arr, char_size);
int int_arr[] = {5, 3, 2, 4, 0, 6, 14, -4, 0, 32, 2, -1};
int int_size = sizeof(int_arr) / sizeof(int_arr[0]);
print_int_array((int*)int_arr, int_size);
binary_insert_sort((void*)int_arr, int_size, sizeof(int), int_compare);
print_int_array((int*)int_arr, int_size);
Person p_arr[] = {{3, "fabio"}, {5, "marco"}, {0, "giulio"}, {2, "alberto"}, {1, "gabri"}};
int p_size = sizeof(p_arr)/sizeof(p_arr[0]);
for(int i =0;i<p_size;i++) print_person(&(p_arr[i]));
// binary_insert_sort(p_arr, p_size, sizeof(Person), person_int_cmp);
binary_insert_sort(p_arr, p_size, sizeof(Person), person_string_cmp);
for(int i =0;i<p_size;i++) print_person(&(p_arr[i]));
return 0;
}
But for some reason, the string array just wont sort: i put some debug prints and to me it seems like the problem is that when i compare them, what's really being compared is their address, as the value of comparison in binary_search is always 1 when it's sorting char_arr
Here are the functions being used:
typedef struct {
int id;
char* name;
} Person;
int person_int_cmp(void* p1, void* p2)
{
Person *a = (Person*)p1;
Person *b = (Person*)p2;
return a->id - b->id;
}
int person_string_cmp(void* p1, void* p2)
{
Person *a = (Person*)p1;
Person *b = (Person*)p2;
return strcmp(a->name, b->name);
}
void print_person(void* p){
Person* a = (Person*)p;
printf("%d: %s\n", a->id, a->name);
}
int string_compare(void *a, void *b)
{
char * str1 = (char *)a;
char * str2 = (char *)b;
return strcmp(str1, str2);
}
int int_compare(void *n1, void *n2)
{
int n01 = *(int *)n1, n02 = *(int *)n2;
return n01 - n02;
}
void print_int_array(int *arr, long size)
{
printf("\t[");
for (int i = 0; i < size; i++) printf("%d -> ", arr[i]);
printf("]\n");
}
void print_string_array(char** arr, long size)
{
printf("\t[");
for (int i = 0; i < size; i++) printf("%s -> ", arr[i]);
printf("]");
}
Am i treating the string array in some wrong way i am not aware of? What's even weirder to me is that it's able to correctly sort the Person array by the names (which are strings) but not an array of just strings
If anyone can point me what i'm doing wrong thanks so much

Related

How to make C functions agnostic to data type

I have been working on a simple library in C to handle arrays, dynamic arrays and linked lists. As a starting point I have been working on a function to pop a user defined index from an array. For starters I am working an an integer array and came upon this solution.
int pop_int_array(int *array, int index, int size) {
int type = sizeof(int);
if (index >= size) return 0;
unsigned char *dst = (unsigned char*) array + index + type;
memmove(dst, dst + type, type * (size - index - 1));
return 1;
}
int main(int argc, const char * argv[]) {
int a[5] = {1, 2, 3, 4, 5};
pop_int_array(a, 2, 5);
for (int i = 0; i < 4; i++) {
printf("%d\n", a[i]);
}
}
This yields the following output as expected
1
2
4
5
If I wanted to pop a float array, I would need to create a new function titled pop_float_array that copied all of the contents from pop_int_array but instead of using int type = sizeof(int) I would replace it with int type = sizeof(float), and of course the array data type would be a float instead of an int. However, this violates a core principle of software development by repeating the same code several times, where each time would be an implementation for a data type. In order to get around this I re-wrote the function in the following way where the array is declared as a void data type. In addition I cast the array to a void * in the main program before or during the process of passing it to the function.
int pop_array(void *array, int index, int size, int type) {
if (index >= size) return 0;
unsigned char *dst = (unsigned char*) array + index + type;
memmove(dst, dst + type, type * (size - index - 1));
return 1;
}
int main(int argc, const char * argv[]) {
int a[5] = {1, 2, 3, 4, 5};
pop_array((void *)a, 2, 5);
for (int i = 0; i < 4; i++) {
printf("%d\n", a[i]);
}
float b[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
pop_array((void *)a, 2, 5);
for (int i = 0; i < 4; i++) {
printf("%f\n", a[i]);
}
}
As expected this yields
1
2
4
5
1.1
2.2
4.4
5.5
This allows me to only create and maintain one function. However, this seems so simple that I am surprised it is not a more widely used technique, or at least that I am aware of in my little experience. Is there some consequence of writing the function this was or casting the array to a void * that I am not seeing, or is this a legitimate way to make a function type agnostic in C?
The way you're doing this is fine. But you do have one mistake:
unsigned char *dst = (unsigned char*) array + index + type;
You're not moving up by the right amount. This should be:
unsigned char *dst = (unsigned char*) array + (index * type);
Also, you don't need to cast to void * when you call the function:
pop_array(a, 2, 5, sizeof(int));
This technique is used in the qsort function which can sort an array of an arbitrary type. It's signature is as follows:
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
As with your function, it takes a void * to the start of the array as well as the number of elements and the size of each element. It also takes a function pointer whose job is to perform the type specific comparison.

Small generic programming issue in C

So I'm starting to understand the basics of generic programming in C. I'm currently building a program that says if a value occurs or not in a given sequence of number.
I think that the bug occurs in the cmpValues function. Would anyone point it out? (for example, for want=4 and v={1,2,3,4,5}, the program says that want is not in v)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void *search(const void *x, const void *t, int n, int d, int (*cmpValues)(const void *, const void *)){
char *p = (char *)t;
int i;
for(i=0;i<n;++i)
if(cmpValues(x,p+i*d))
return p+i*d;
return NULL;
}
int cmpValues(const void *a, const void *b){
if((char *)a == (char *)b)
return 1;
return 0;
}
int main() {
FILE *f = fopen("datein.txt", "r");
FILE *g = fopen("dateout.txt", "w");
int *v, n, i, want;
fscanf(f, "%d", &n);
v = (int *)malloc(n * sizeof(int));
for(i = 0; i < n; ++i)
fscanf(f, "%d", v + i);
fscanf(f, "%d", &want);
if(search(&want, v, n, sizeof(int), cmpValues))
fprintf(g, "The value %d is found at position %d.\n\n", want, search(&want, v, n, sizeof(int), cmpValues));
else
fprintf(g, "The value does bot occur in the given sequence.\n\n");
return 0;
}
In cmpValues, you are comparing 2 objects pointed by 2 void pointers (i.e. you don't know their type, nor their size). Let's assume we are having ints, and that an int has 4 bytes, which is usually the case.
Just for the sake of it, let's assume that the a pointer has value 0x100 (i.e. points to a int from 0x100 to 0x103, inclusive) and b pointer has a value of 0x104 (i.e. points to the int from 0x104 to 0x107).
Now, you are converting them to char* (char has 1 byte) and compare the value of the pointers. Now, the type of the pointer does not matter in comparisons. In that comparison, you will compare memory addresses (in my example, 0x100 and 0x104). Obviously, the only way the function will return 1 is if the pointers would point to the same variable.
Now, in order to fix it, you should compare the values at the memory addresses pointed by your pointers. However, simply dereferencing the pointers:
*((char *)a) == *((char *)b)
won't be enough, since this would compare just the first byte of a with the first byte of b (under the assumption that char has 1 byte). Also, you can't dereference void*.
So, you need to iterate over your variables and compare them byte by byte (this assumes that you know the size of the data type):
int comp(void *a, void *b, int size) {
// convert a and b to char* (1 byte data type)
char *ca = a;
char *cb = b;
// iterate over size bytes and try to find a difference
for (int i = 0; i < size; i++) {
if (*(ca + i) != *(cb + j)) {
return 0;
}
}
// if no difference has been found, the elements are equal
return 1;
}
side note: you don't need to call cauta twice in main.

Generic quicksort not working

I'm trying to make a generic quicksort function, and I fail to understand what's wrong with what I'm doing, because it's not working properly.
Here is my code:
typedef bool (*CmpFunction)(void*, void*);
void swap(void *c1, void *c2)
{
assert(c1 && c2);
int c = *(int*)c1;
*(int*)c1 = *(int*)c2;
*(int*)c2 = c;
}
void quick_sort(void* a, int n, CmpFunction swap)
{
int p, b = 1, t = n - 1;
if (n < 2)
return;
swap((char*)a, (char*)a+n/2);
p = *(int*)a;
while(b <= t) {
while(t >= b && (char*)a + t >= p )
t--;
while(b <= t && (char*)a + b < p)
b++;
if ( b < t)
swap((char*)a+(b++), (char*)a+(t--));
}
swap((char*)a, (char*)a+t);
quick_sort(a, t, swap);
n=n-t-1;
quick_sort(a + t + 1, n, swap);
}
While the original quicksort function, without me trying to make it generic is:
void quick_sort(int a[], int n)
{
int p, b = 1, t = n - 1;
if (n < 2)
return;
swap(&a[0], &a[n/2]);
p = a[0];
while(b <= t) {
while(t >= b && a[t] >= p )
t--;
while(b <= t && a[b] < p)
b++;
if ( b < t)
swap(&a[b++], &a[t--]);
}
swap(&a[0], &a[t]);
quick_sort(a, t);
n=n-t-1;
quick_sort(a + t + 1, n);
}
void swap(int *c1, int *c2)
{
int c = *c1;
*c1 = *c2;
*c2 = c;
}
I'm using this main():
int main(){
char b[] = {'a','t','b','c','y','s'};
int c[] = {1,4,6,3,5,7};
quick_sort(c, 6, &swap);
for (int i=0;i<6;i++)
printf("%d | ", c[i]);
return 0;
}
Now we all agree that the output should be:
1, 3, 4, 5, 6, 7
which is indeed what I get when running the NOT generic function.
When I run my generic(upper) function I get basically trash.
You all have any ideas where I'm wrong? :)
The most obvious issue: Your input data is an int array, typecasted into a void * pointer, then forced into a char * pointer:
swap((char*)a, (char*)a+n/2);
Here you force that into a char * pointer, and jumping n/2 into it.
char * is an array of 1 byte size elements
int * is an array of 2, 4 or 8 byte size elements depending on compiler/OS/CPU.
So char *a +1, void give you the second byte of the first element of the initial array.
qsort is a generic sorting function. You give it an array, the size of the elements in the array, the number of elements, and a comparison function.
typedef int(*compare)(const void*, const void*);
void quicksort(void *base, size_t num_elements, size_t width, compare *cmp);
To move through the array the sorting function needs to know the width of each element so it can do the pointer arithmetic correctly. An array of char will be 1 byte per element. An array of int is probably 4 bytes. double will be 8. base[4] of a char array is base + 4*1, but it's base + 4*4 for an int array. Ultimately base[n] is base + (n * width).
To avoid making assumptions about the data in the elements, or how you want them sorted, the compare is used to compare elements for sorting. It returns < 0 if a < b, 0 if a == b and > 0 if a > b. This allows it to be as simple as return a - b for most numbers.
An example function for comparing integers:
int cmp_int(const void* _a, const void* _b) {
/* Do the casting separately for clarity */
int *a = (int *)_a;
int *b = (int *)_b;
return *a - *b;
}
There's no need to pass in a swap function. So long as you know the size of the elements a single swap function will serve. The one from #HonzaRemeš' answer works.
void swap(void * a, void * b, size_t size) {
/* Temp buffer large enough to contain an element */
char tmp[size];
memcpy(tmp, a, size);
memcpy(a, b, size);
memcpy(b, tmp, size);
}
With all this in mind, your function is not being given the element size (ie. width) so it cannot correctly move through the array. It's also unnecessarily passing in a swap function, but there's no need for this if you know the size of the elements. And you're lacking a proper comparison function to compare elements. Not much of a generic sort function if it can't compare things to sort them.
You are trying to do something the C language is not very appropriate for. If you want to do it, you need some background knowledge about pointer arithmetics.
Specifically, for a T *, where T is a type with size N (sizeof(T) == N),
T * ptr;
ptr = (T *) 0x0100;
ptr = ptr + 1;
// ptr now has value 0x100 + N
That means you can't have a generic function which will operate on data arrays without knowing the size of the array element.
So I suggest you rewrite your quick_sort and swap functions to incorporate size parameters. You then cast your pointers to char * and use the size parameter to make the functions work correctly. Example swap function follows.
void swap(void * c1, void * c2, size_t size) {
char tmp[size]; // temporary buffer big enough to contain c1 data
memcpy(tmp, c1, size);
memcpy(c1, c2, size);
memcpy(c2, tmp, size);
}
Modifying quick_sort is left as an exercise :). Remember though that when you don't know the size of your data, you must use memcpy(dst, src, size) instead of dst = src, you must use memcmp(a1, a2, size) >= 0 instead of a1 >= a2 and that your pointer access must be multiplied by size (exerpt from quick_sort follows):
EDIT: #Schwern points out in the comments why using memcmp() may not work. Comparing values of unknown size and format (endianness, float X int) would probably require a generic comparison function (which would likely be next to impossible to write). That takes us back to C's ill-suitedness for this task.
void quick_sort(void *a, int n, size_t size) {
char[size] p;
int b = 1, t = n - 1;
if(n < 2)
return;
// Using new swap with 'size' parameter
swap(&a[0], &((char *)a)[n / 2 * size], size);
// or swap((char *)a + 0, (char*)a + (n / 2 * size), size);
memcpy(p, a, size);
while(b <= t) {
while(t >= b && memcmp((char *)a[t * size], p, size) >= 0) {
...
}
You can then write wrapper macros to pass the size parameter to the quick_sort function.
#define QSORT(arr, n) quick_sort((arr), (n), sizeof((arr)[0]))

Generic insertion sort in C

I have coded a generic insertion sort in C, and it works really fine.
But, On my function of insertion sort, it gets a void** arr,
and on its signature it gets a void* arr, otherwise, it doesn't work.
Why is it so?
Do we have any other ways to code the insertion sort to be generic?
The Full code is here:
#include <stdio.h>
#include <malloc.h>
#define SIZE 10
int cmp(void* elm1, void* elm2);
void insertionSort(void* arr, int size);
int main()
{
int arr[] = {5, 8, 2, 3, 15, 7, 4, 9, 20, 13};
int arr2[] = {1};
int i;
for (i = 0; i < SIZE; i++)
printf("%d ", arr[i]);
printf("\n");
insertionSort(&arr, SIZE);
for (i = 0; i < SIZE; i++)
printf("%d ", arr[i]);
return 0;
}
void insertionSort(void** arr, int size)
{
int i = 1;
int j;
void* temp;
while (i < size)
{
if (cmp(arr[i], arr[i-1]) == -1)
{
temp = arr[i];
j = i - 1;
while (j >= 0 && cmp(arr[j], temp) == 1)
{
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
i++;
}
}
int cmp(void* elm1, void* elm2)
{
if ((int)elm1 == (int)elm2)
return 0;
else if ((int)elm1 > (int)elm2)
return 1;
else
return -1;
}
The code as it is, is undefined, because of multiple problems. It just happens to work, because on your system the size of the pointer is the same as the size of the type int.
You code will not compile without warnings (if you enable them). The function insertionSort and it's prototype must have the same type.
You should change the type in the function definition to
void insertionSort(void* arr, int size)
And then cast the pointer arr, to an appropriate type. Since this is a generic sort, like qsort(), the only realistic option is a cast to char*. This means you will also have to pass the size of the type into the function, so the pointer can be incremented correctly. This will require you to change the function drastically.
So, the function prototype should really be the same as qsort:
void Sort(void* arr, size_t size , size_t object_size , int(*)( const void* , const void* ))
The problem is that integers are not pointers, so your test array is of type *int or int[]. But in your function, you don't know that and you try to make your code work with pointers. So you expect * void[]. If you change your temp variable to int, you don't need the ** in the signature. The same way, if you want to keep the "generic" (as you call), you need an array of *int.
Basically, in C you cannot write a function working out of the box for both primary types and pointers. You need some tricks. Have a look at this stackoverflow, maybe it will help.

string_comparator in C

Okay so I need to several quite long strings in C. So I say to myself "why, you'd better use that handy dandy qsort function! Better write yourself a string_comparator for it!"
So of course I do and here she is:
int string_comparator(const void* el1, const void* el2) {
char* x = (char*) el1;
char* y = (char*) el2;
int str_len = strlen(x);
int i = 0;
for (; i < str_len; i++) {
//when there are non-equal chars
if (x[i] != y[i]) {
break;
}
}
return x[i] - y[i];
}
So of course I pass my handy dandy string_comparator function to the C qsort function as such:
qsort(list.words, list.num_words, sizeof(char*), string_comparator);
list is a struct that holds a char** (words) and ints which refer to the number of words held by it (such as num_words)
Now I have the problem where my list is not getting sorted alphabetically like I had hoped! I put a bunch of printf statements in my comparator and it printed out garbage values for the strings every time so I'm fairly sure that is the problem. But why is that the problem?? I've used qsort before (never to sort words..just sorting characters) and from what I understand this should work...What's going wrong here?
I appreciate any suggestions!
This is a common mistake when using qsort(). Here are the corrections:
char *x = *(char **) el1;
char *y = *(char **) el2;
Because list.words has type char **, not type char *, right?
Another example of qsort()
Here's how you sort an array of int with qsort():
int int_comparator(const void *el1, const void *el2)
{
int x = *(int *) el1;
int y = *(int *) el2;
return x - y;
}
void sort_ints(int *a, size_t n)
{
// these two lines are both "correct"
// the second line is more "obviously correct"
// qsort(a, n, sizeof(int), int_comparator);
qsort(a, n, sizeof(*a), int_comparator);
}
Now, if you go through and replace int with char *, you have to replace int * with char **.

Resources