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
I have an array of char* like this
aob3l
gou5!
oib1k
llp6d
with every 3rd index a number.
I want to use qsort() to sort that list according to those numbers.
A sorted list would look like this
oib1k
aob3l
gou5!
llp6d
This is how I'm trying to implement it.
qsort(string_mix, (size_t) (k - 1), sizeof(char), compare_at3);
My compare_at3 function looks like this
int compare_at3(const void* a, const void* b){
static int k = 1;
const char *ia = a;
const char *ib = b;
printf("In compare_at3 %d iter\n", k++);
return ((ia[3] - '0') - (ib[3] - '0'));
}
And my question is,
what am I doing wrong to get SIGSEGV segfault errors?
My best guess is that this line const char *ia = a; isn't doing what I think it's doing.
I'm using * in *ia so I can subscript int as its index but maybe *ia isn't the char* like aob3l.
EDIT1: Sorry, I didn't mention what exactly string_mix is.
It's a 2D array of those strings in the question defined like
char* string_mix[MAX_NO_OF_STRINGS]
char s1_formatted[strlen(s1)];
char s2_formatted[strlen(s2)];
formatted(s1, s1_formatted);
formatted(s2, s2_formatted);
char s1_s[26][MAX_LEN_A];
char s2_s[26][MAX_LEN_B];
for(char ch = 'a'; ch <= 'z'; ch++) {
sprintf(s1_s[ch - 'a'], "%c%d%s", ch, s1_n[ch - 'a'], t_stringer1[ch - 'l']);
sprintf(s2_s[ch - 'a'], "%c%d%s", ch, s2_n[ch - 'a'], fs_stringer1[ch - 'o')]);
}
char* string_mix[MAX_NO_OF_STRINGS];
int k = 0;
for(int i = 0; i < 26; i++){
if(s1_s[i][3] - '0' != 0){
string_mix[k] = s1_s[i];
k++;
}
else if(s2_s[i][3] - '0' != 0){
string_mix[k] = s2_s[i];
k++;
}
}
The call to qsort is incorrect. The third parameter should be the size of each element. A correct call could be:
qsort(string_mix, (size_t) (k - 1), sizeof *string_mix, compare_at3);
or, if you prefer, and assuming string_mix is an array of char*:
qsort(string_mix, (size_t) (k - 1), sizeof (char*), compare_at3);
string_mix is an array of pointers - each array element is a pointer. qsort() calls need to pass the size of the element. #Klas Lindbäck
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
char* string_mix[MAX_NO_OF_STRINGS];
// qsort(string_mix, (size_t) (k - 1), sizeof(char), compare_at3);
// I suppose k-1 represents the count of elements used
size_t number_of_elements = k - 1;
qsort(string_mix, number_of_elements, sizeof string_mix[0], compare_at3);
what am I doing wrong to get SIGSEGV segfault errors?
Wrong type assumed in compare function.
The compare function receives 2 pointers. Each is a pointer to an element of the array. In OP's case this is a pointer to a pointer.
int compare_at3(const void* a, const void* b){
static int k = 1;
printf("In compare_at3 %d iter\n", k++);
// const char *ia = a;
const char **ia = (const char **) a;
const char **ib = (const char **) b;
const char *sa = *ia;
const char *sb = *ib;
// Code could insure sa, sb are long enough
if (sa[0] == '\0' || sa[1] == '\0' || sa[2] == '\0') Handle_OddCase();
if (sb[0] == '\0' || sb[1] == '\0' || sb[2] == '\0') Handle_OddCase();
// The classic compare indium is below, it never overflows.
return (sa[3] > sb[3]) - (sa[3] < sb[3]);
}
Note: Concerning OP comment "It's a 2D array of those strings in the question defined like". string_mix is better described as a 1D array of pointers.
char* string_mix[MAX_NO_OF_STRINGS];
You convert a void pointer to a char pointer and access index 3 without knowing anything about the memory, the char pointer is pointing to inside the function. This is bad style and most probably the reason for your SIGSEGV, because you cannot access ia[3].
Furthermore, you say, you want to sort according to the 3rd letter... this would be ia[2], if ia were the correct pointer to the char array.
Please post your code... how did you create the array of char*?
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.
I can't figure out how to use qsort. I want to sort an array of strings. Like so:
John Adam
Adam -> John
Stacy Stacy
However, nothing I do seems to work. I've tried copying exactly what others have used (about 5 different qsort functions from various sources) and nothing has worked. I have one for int's that works (backwards but at least it works).
Here's the necessary code I have:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char name[80];
int age;
} RECORD;
RECORD record[25];
int main (int argc, char *argv[80]){ // Using command line to get my strings
int i = 2, j;
for(j = 0; j < (argc / 2); j++) //Converting and storing my ages
{
record[j].age = atoi(argv[i]);
i = i + 2;
}
int p, q = 1;
for(p = 0; p < (argc / 2); p++)
{
strcpy(record[p].name, argv[q]);
q = q + 2;
}
}
int compareByName(const void* a, const void* b) //The qsort that doesn't work at all
{
const char *ia = (const char *)a;
const char *ib = (const char *)b;
return strncmp(ia, ib, 25);
}
int compareByAge (const void * a, const void * b) //My other qsort that works backwards
{
RECORD *RECORDA = (RECORD *)a;
RECORD *RECORDB = (RECORD *)b;
return ( RECORDB->age - RECORDA->age );
}
void printRecords(RECORD r[], int num){
//printing stuff here
double size = sizeof r[0];
double count = sizeof(r)/size; //My qsort with size stuff, doesn't work
qsort(r, count, size, compareByName); // if I do it the same as the other
qsort (r, 25, sizeof(RECORD), compareByAge); //My other qsort that works backwards
//more printing stuff here
}
You don't have an array of strings, you have an array of RECORDs, and it sounds like you want to sort that array based on the strings in the name array of the records. So you want something like:
int compareByName(const void *a_, const void *b_) {
RECORD *a = a_, *b = b_;
return strcmp(a->name, b->name);
}
and then you sort with
qsort (r, 25, sizeof(RECORD), compareByName);
Okay, I see several issues here.
It's worth noting that sizeof is evaluated at compile time, so you can't use sizeof(r) to determine the size of a dynamically-sized array you were passed. I'm going to guess that's why num is passed in to printRecords.
As #Chris points out, you're sorting RECORD structures rather than character pointers, so the comparison function and the qsort call both need to take that into account.
You have the subtraction reversed in the age comparison - it needs to return a negative number if the left side is less than the right side, so use RECORDA->age - RECORDB->age.
First off (this is why your strings aren't sorting), double count = sizeof(r)/size; is wrong. sizeof(r) isn't doing what you expect it to. You'll need to pass the size of the array to printRecords() as described in this question:
How to get the length of array in C? is "sizeof" is one of the solution?
Second off,
int compareByAge (const void * a, const void * b) //My other qsort that works backwards is backwards because you're doing it backwards. Comparator functions always return A - B, not B - A.
This code take 5 strings and sort them in ascending way.
void swap (char data[5][255], int i, int j) {
char temp[255];
strcpy(temp,data[i]);
strcpy(data[i],data[j]);
strcpy(data[j],temp);
}
void sort (char data[5][255], int n) {
// * : first address contact
int i, j;
for(i = 0; i < n-1; i++)
for( j = i+1; j > 0; j--)
if(strcmp(data[j-1],data[j])>0)
{
printf("%s",data[j-1]);
swap(data, j-1, j);
}
}
int main() {
char strings[5][255];
char comp[255];
int i, n;
n = sizeof(strings)/sizeof(comp);
printf("Enter 5 strings, max 255 chars each:\n");
for(i=0; i < n; i++)
scanf("%s",strings[i]);
sort(strings, n);
printf("Sorted data:\n");
for(i=0; i < n-1; i++)
printf("%s, ",strings[i]);
printf("%s.\n",strings[i]);
return 0;
}
In addition of that, how can I possibly parse my static array string[5][255] to function by using pointer?
I tried that, for example,
void sort ( char **data, int i ) { ... }
but it throws out error like this.
incompatible pointer types passing 'char [5][255]' to parameter of type 'char **'
Is there anything I can parse my array using pointer?
Since array parsed to function its first address(pointer), I thought function will accept those expression. Please give me some advice to understand.
The parameter you must pass is not a pointer to a pointer char**, but a pointer to a char array char(*)[255]
void sort (char (*data)[255], int n)
When you pass an array, you can omit the size of the first dimension
void sort (char data[][255], int n)
which is equivalent to char(*)[255].
char** is a pointer, which points to another pointer. Whereas char(*)[255] is a pointer, which points to an array.
You have to understand the difference between: char **data vs char (*data)[255]
Those are two different types because the allocation of memory may be different:
When char **data is used pointer arithmetic may not work properly, meaning data could be scattered all over the memory
When char (*data)[255] is used pointer arithmetic works perfectly because all elements of array are adjacent to each other
As I wrote you in the comment char[][] doesn't decay to char** but it decays to char(*)[] ( char (*data)[255] in your case) as the first element decay in a pointer which is not a "pointer to a pointer" but a pointer to an array of 255 characters. It is possible to use a char** pointer if you do something like this (c++):
char **array = new char *[N];
for(int i = 0; i<N; i++)
array[i] = new char[N];
or using the malloc (c).
As #newact suggests it is important to distinguish between
char *data[255] -> char *[255]data = => "data is an array of 255 pointers to char"
And
char (*data)[255] -> char [255](*data) => "data is a pointer to an array of 255 chars"
Try to make the following changes:
void swap ( char *data, int i, int j)
*(data + index1*255 + index2)