I tried to put program a generic method in C to identify the biggest element of an array.
At first, I programmed this:
int compare(const void* a, const void* b) {
if(a < b)
return 0;
return 1;
}
int main(void) {
int (*prt)(const void*, const void*);
prt=compare;
printf("%i",(*prt)(1,1));
return EXIT_SUCCESS;
}
This works fine, but if I try to put the function pointer prt
in a new method, I do not know how to handle it.
Addionally i dont know how to handle void* types.
void* maximum(int len, void* array, size_t size, int (*cmp)(const void*, const void*));
int compare(const void* a, const void* b) {
if(a < b)
return 0;
return 1;
}
int main(void) {
int (*prt)(const void*, const void*);
prt=compare;
printf("%i",(*prt)(1,1));
int array[6] = {3, 1, 0 , 4 , 3, 9};
maximum(len,array,0,prt);
return EXIT_SUCCESS;
}
void* maximum(int len, void* array, size_t size, int (*cmp)(const void*, const void*)) {
void* temp;
temp = array[0];
printf("%i",a);
int i;
for(i = 1; i < len; i++) {
if((*cmp)(temp,array[i]) == 0) {
temp = array[i];
}
}
return 0;
}
There are many errors... e.g. the variable temp or if((*cmp)(temp,array[i]) == 0).
Do you have an idea how to use not defined datatypes?
You are comparing addresses instead of values:
int compare(const void* a, const void* b) {
if(a < b)
return 0;
return 1;
}
Should be:
int compare(const void* a, const void* b) {
if(*(int *)a < *(int *)b)
return 0;
return 1;
}
Here is an example that can be taken as a base code.
#include <stdio.h>
int cmp( const void *a, const void *b )
{
return *( const int * )a < *( const int * )b;
}
void * maximum( const void *array, size_t size, size_t len,
int cmp( const void *, const void *) )
{
const void *max = array;
size_t i = 1;
for ( ; i < size; i++ )
{
if ( cmp( ( const char * )max, ( const char * )array + i * len ) )
{
max = ( const char * )array + i * len;
}
}
return ( void * )max;
}
int main(void)
{
int array[] = { 3, 1, 0 , 4 , 3, 9 };
int *max =
maximum( array, sizeof( array )/ sizeof( *array ), sizeof( int ), cmp );
printf( "Maximum = %d\n", *max );
return 0;
}
The output is
Maximum = 9
#include <stdio.h>
#include <stdlib.h>
void *maximum(int len, void* array, size_t size, int (*cmp)(const void*, const void*));
//comparison function must know about type.
//Because it is not known for functions like memcmp that type what is the layout.
int intcmp(const int *x, const int *y){
return *x < *y ? -1 : *x > *y;
}
int main(void) {
int array[6] = {3, 1, 0 , 4 , 3, 9};
int *p = maximum(sizeof(array)/sizeof(*array), array, sizeof(*array), (int (*)(const void*,const void*))intcmp);
printf("%d\n", *p);//9
return EXIT_SUCCESS;
}
void *maximum(int len, void *array, size_t size, int (*cmp)(const void*, const void*)) {
int i;
void *temp = array;
for(i = 1; i < len; i++) {
if(cmp((char*)array + size*i, temp)>0) {
temp = (char*)array + size*i;
}
}
return temp;
}
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 10 months ago.
Improve this question
Can anyone tell me what am I doing wrong in this generic quicksort code following this pseudocode Quicksort & Partition, the algorithm works, because I have already done it with integers only without the compare function by passing an int array to the quicksort and partition functions, but I have tried to make it work for both int and strings. In this code I have tested only the int values, but the code doesn't work, the output is the initial value of the array, it's the same exact thing for the strings I get the same initial array as an output. I have commented the string part because they get sorted the same way as the integers. This is the integer code that works Integer working code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//prototipi delle funzioni
typedef int (*compare_function)(const void *, const void *);
void generic_quicksort(void *v, int i, int f, size_t size, compare_function compare);
void generic_swap(void *a, void *b, size_t size);
int generic_partition(void *v, int i, int f, size_t size, compare_function compare);
void print_int_array(const int *array, size_t len) {
size_t i;
for (i = 0; i < len; i++)
printf("%d | ", array[i]);
putchar('\n');
}
//funzione di confronto
int compare_int(const void *, const void *);
int compare_str(const void *a, const void *b) {
const char **ia = (const char **)a;
const char **ib = (const char **)b;
return strcmp(*ia, *ib);
/* strcmp functions works exactly as expected from
comparison function */
}
void print_cstring_array(char **array, size_t len) {
size_t i;
for (i = 0; i < len; i++)
printf("%s | ", array[i]);
putchar('\n');
}
int main() {
int v[] = { 5, 4, 3, 2, 1 };
char *strings[] = { "Zorro", "Alex", "Celine", "Bill", "Forest", "Dexter" };
int n = sizeof(v) / sizeof(int);
print_int_array(v, n);
generic_quicksort((void *)v, 0, n - 1, sizeof(int), compare_int);
print_int_array(v, n);
/*
int s = sizeof(strings) / sizeof(*char);
print_cstring_array(strings, s);
generic_quicksort((void *)strings, 0, s - 1, sizeof(*char), compare_str);
print_cstring_array(strings, s);
*/
return 0;
}
int compare_int(const void *a, const void *b) {
return *((int*)a) - *((int*)b);
}
void generic_quicksort(void *v, int i, int f, size_t size, int (*comp)(const void *, const void *)) {
if (i >= f)
return;
int p = generic_partition(v, i, f, size, comp);
generic_quicksort(v, i, p - 1, size, comp);
generic_quicksort(v, p + 1, f, size, comp);
}
void generic_swap(void *a, void *b, size_t size) {
void *tmp = malloc(size);
memcpy(tmp, a, size);
memcpy(a, b, size);
memcpy(b, tmp, size);
free(tmp);
}
int generic_partition(void *v, int i, int f, size_t size, int (*comp)(const void *, const void *)) {
void *x = malloc(size);
int k, j;
memcpy(x, v + (i * size), size);
k = i - 1;
for (j = i; j <= f - 1; j++) {
if (comp(v + (j * size), x) <= 0) {
k++;
generic_swap(v + (k * size), v + (j * size), size);
}
}
generic_swap(v + ((k + 1) * size), v + (f * size), size);
free(x);
return (k + 1);
}
There are multiple problems in the code:
int n = sizeof(v) / sizeof(int); is risky: there is a silent assumption about the type of v. You should write int n = sizeof(v) / sizeof(*v);
The convention to pass the indices of the first and last elements of the slice is confusing and not idiomatic in C, you should pass the index of the first element and the index of the element after the last one. This allows for unsigned index types and empty arrays.
v + (j * size) uses void pointer arithmetics, which is an extension not available on all systems. Use unsigned char pointers for this.
the comparison function for integers has undefined behavior for large absolute values because subtracting them may cause an arithmetic overflow. You should use this instead:
int compare_int(const void *a, const void *b) {
int ia = *(const int *)a;
int ib = *(const int *)b;
return (ia > ib) - (ia < ib);
}
generic_swap uses malloc and memcpy. This causes much overhead for small elements, you should use a simple loop:
void generic_swap(void *a, void *b, size_t size) {
unsigned char *pa = (unsigned char *)a;
unsigned char *pb = (unsigned char *)b;
while (size-- > 0) {
unsigned char c = *pa;
*pa++ = *pb;
*pb++ = c;
}
}
The generic_partition in the reference uses the last element as the pivot, but you initialize x from the first element. You should write memcpy(x, v + (f * size), size);. This is causing the failure. The current code might work by coincidence for the int version. Using the first or the last element as a pivot causes worst case behavior on sorted arrays.
Here is a modified version:
#include <stdio.h>
#include <string.h>
//prototipi delle funzioni
typedef int (*compare_function)(const void *, const void *);
void generic_quicksort(void *v, int i, int f, size_t size, compare_function compare);
//funzione di confronto
int compare_int(const void *a, const void *b) {
int ia = *(const int *)a;
int ib = *(const int *)b;
return (ia > ib) - (ia < ib);
}
int compare_str(const void *a, const void *b) {
const char *sa = *(const char * const *)a;
const char *sb = *(const char * const *)b;
return strcmp(sa, sb);
}
void print_int_array(const int *array, size_t len) {
size_t i;
if (len > 0) {
printf("%d", array[0]);
for (i = 1; i < len; i++)
printf("| %d", array[i]);
}
putchar('\n');
}
void print_cstring_array(const char * const *array, size_t len) {
size_t i;
if (len > 0) {
printf("%s", array[0]);
for (i = 1; i < len; i++)
printf(" | %s", array[i]);
}
putchar('\n');
}
static void generic_swap(void *a, void *b, size_t size) {
unsigned char *pa = (unsigned char *)a;
unsigned char *pb = (unsigned char *)b;
while (size-- > 0) {
unsigned char c = *pa;
*pa++ = *pb;
*pb++ = c;
}
}
static int generic_partition(void *v, int i, int f, size_t size,
int (*comp)(const void *, const void *))
{
unsigned char *p = (unsigned char *)v;
int j, k = i;
// using first element as pivot
for (j = i + 1; j < f; j++) {
if (comp(p + j * size, p + i * size) <= 0) {
k++;
generic_swap(p + k * size, p + j * size, size);
}
}
/* swap the pivot to the end of the left part */
generic_swap(p + i * size, p + k * size, size);
return k;
}
void generic_quicksort(void *v, int i, int f, size_t size,
int (*comp)(const void *, const void *))
{
if (f > i + 1) {
int p = generic_partition(v, i, f, size, comp);
generic_quicksort(v, i, p, size, comp);
generic_quicksort(v, p + 1, f, size, comp);
}
}
int main() {
int v[] = { 5, 4, 3, 2, 1 };
int n = sizeof(v) / sizeof(*v);
const char *strings[] = { "Zorro", "Alex", "Celine", "Bill", "Forest", "Dexter" };
int s = sizeof(strings) / sizeof(*strings);
print_int_array(v, n);
generic_quicksort((void *)v, 0, n, sizeof(*v), compare_int);
print_int_array(v, n);
print_cstring_array(strings, s);
generic_quicksort((void *)strings, 0, s, sizeof(*strings), compare_str);
print_cstring_array(strings, s);
return 0;
}
Note that choosing the first or the last element as the pivot leads to worst case complexity for a sorted array. The depth of recursion for generic_quicksort will be the length of the array, potentially causing a stack overflow.
Here is a modified version that is protected against this, but still has quadratic time complexity on a sorted array:
void generic_quicksort(void *v, int i, int f, size_t size,
int (*comp)(const void *, const void *))
{
while (f > i + 1) {
int p = generic_partition(v, i, f, size, comp);
if (p - i < f - p) {
generic_quicksort(v, i, p, size, comp);
i = p + 1;
} else {
generic_quicksort(v, p + 1, f, size, comp);
f = p;
}
}
}
I was doing a question about finding an element in an array using function pointer concept. But I am facing some issues regarding this.
Here is my code.
#include <stdio.h>
#include <stdbool.h>
bool compare( const void* a, const void* b)
{
return (*(int*)a == *(int*)b);
}
int search(const void * arr, int arr_size, int ele_size, void* x, bool compare(const void*, const void*))
{
char* ptr = *(char**)arr;
int i;
for (i = 0; i < arr_size; i++)
{
if (compare(ptr + i * ele_size, x))
{
return i;
}
}
return -1;
}
int main()
{
int arr[] = { 2, 5, 7, 90, 70 };
int n = sizeof(arr) / sizeof(arr[0]);
int x = 7;
printf("Returned index is %d ", search(arr, n, sizeof(int), &x, compare));
return 0;
}
The code is compile fine but not giving any output?
What is wrong with this code?
No need to de-reference arr, just alter its type to do the pointer math.
// char *ptr = *(char**) arr;
const char *ptr = arr;
Minor: Best practice to use const here.
I'm working for this ADT project and i need to implement the insertion sort algorithm and verify that it works fine in an appropriate test function, that apply the algorithm to an array of double, a string and a struct.
I'm using as a guideline this pseudocode:
procedure InsertionSort(a, n)
for i <- 1, (n-1) do
j <- 1
while (j>0) and (a[j] < a[j-1]) do
Swap(a, j-1, j)
end while
end for
end procedure
I can't understand what the problem is.
The test function gives me error on the first assert () [ in test_sort_algorithm(...) ], therefore telling me that the algorithm is not working properly. But I can't understand where the error is. I've tried to recreate the algorithm for a normal array, without using a void pointer, and everything works. So I guess my problem is that I didn't understand the use of void pointers.
Can anyone please help me understand what's wrong with my Insertion sort algorithm?
Thank you.
This is my attempt:
/**
* \brief Sorts the given array according to the insertion sort algorithm.
*
* \param base Pointer to the start of the input array.
* \param n Number of elements in the input array.
* \param size The size (in bytes) of each element of the array.
* \param cmp Pointer to the comparison function used to sort the array in
* ascending order.
* The comparison function is called with two arguments that point to the
* objects being compared and must return an interger less than, equal to, or
* greater than zero if the first argument is considered to be respectively
* less than, equal to, or greater than the second.
*/
void upo_insertion_sort(void *base, size_t n, size_t size, upo_sort_comparator_t cmp)
{
size_t i, j;
unsigned char *ptr = base;
for (i = 1; i <= n-1; i++)
{
j = i;
while ( (j > 0) && (cmp(ptr+j*size, ptr+(j-1)*size) < 0) )
{
swap(ptr+(j-1)*size, ptr+j*size, size);
j = j - 1;
}
}
}
void swap(void *a, void *b, size_t n)
{
void *tmp = malloc(n);
if (tmp == NULL) { abort(); }
memmove(tmp, a, n);
memmove(a, b, n);
memmove(b, tmp, n);
free(tmp);
}
upo_sort_comparator_t cmp is a pointer to a comparison function. Declaration:
/** \brief Type definition for comparison functions used to compare two elements */
typedef int (*upo_sort_comparator_t)(const void*, const void*);
As I say before this function must be tested, to see if the algorithm work properly.
Code:
#define N 9
struct item_s
{
long id;
char *name;
};
typedef struct item_s item_t;
static double da[] = {3.0,1.3,0.4,7.8,13.2,-1.1,6.0,-3.2,78};
static double expect_da[] = {-3.2,-1.1,0.4,1.3,3.0,6.0,7.8,13.2,78.0};
static const char *sa[] = {"The","quick","brown","fox","jumps","over","the","lazy","dog"};
static const char *expect_sa[] = {"The","brown","dog","fox","jumps","lazy","over","quick","the"};
static item_t ca[] = {{9,"john"},{8,"jane"},{7,"mary"},{6,"anthony"},{5,"stevie"},{4,"bob"},{3,"ann"},{2,"claire"},{1,"alice"}};
static item_t expect_ca[] = {{1,"alice"},{2,"claire"},{3,"ann"},{4,"bob"},{5,"stevie"},{6,"anthony"},{7,"mary"},{8,"jane"},{9,"john"}};
/* Comparators */
static int double_comparator(const void *a, const void *b);
static int string_comparator(const void *a, const void *b);
static int item_comparator(const void *a, const void *b);
/* Test cases */
void test_sort_algorithm(void (*sort)(void*,size_t,size_t,upo_sort_comparator_t));
static void test_insertion_sort();
int double_comparator(const void *a, const void *b)
{
const double *aa = a;
const double *bb = b;
return (*aa > *bb) - (*aa < *bb);
}
int string_comparator(const void *a, const void *b)
{
const char **aa = (const char**) a;
const char **bb = (const char**) b;
return strcmp(*aa, *bb);
}
int item_comparator(const void *a, const void *b)
{
const item_t *aa = a;
const item_t *bb = b;
return (aa->id > bb->id) - (aa->id < bb->id);
}
void test_sort_algorithm(void (*sort)(void*,size_t,size_t,upo_sort_comparator_t))
{
int ok = 1;
size_t i = 0;
double *da_clone = NULL;
char **sa_clone = NULL;
item_t *ca_clone = NULL;
ok = 1;
da_clone = malloc(N*sizeof(double));
assert( da_clone != NULL );
memcpy(da_clone, da, N*sizeof(double));
sort(da_clone, N, sizeof(double), double_comparator);
for (i = 0; i < N; ++i)
{
ok &= !double_comparator(&da_clone[i], &expect_da[i]);
}
free(da_clone);
assert( ok );
ok = 1;
sa_clone = malloc(N*sizeof(char*));
assert( sa_clone != NULL );
memcpy(sa_clone, sa, N*sizeof(char*));
sort(sa_clone, N, sizeof(char*), string_comparator);
for (i = 0; i < N; ++i)
{
ok &= !string_comparator(&sa_clone[i], &expect_sa[i]);
}
free(sa_clone);
assert( ok );
ok = 1;
ca_clone = malloc(N*sizeof(item_t));
assert( ca_clone != NULL );
memcpy(ca_clone, ca, N*sizeof(item_t));
sort(ca_clone, N, sizeof(item_t), item_comparator);
for (i = 0; i < N; ++i)
{
ok &= !item_comparator(&ca_clone[i], &expect_ca[i]);
}
free(ca_clone);
assert( ok );
}
void test_insertion_sort()
{
test_sort_algorithm(upo_insertion_sort);
}
int main()
{
printf("Test case 'insertion sort'... ");
fflush(stdout);
test_insertion_sort();
printf("OK\n");
return 0;
}
Let's say I have structure like this:
typedef struct MyStruct{
char *string1;
int number1, number2, number3;
char string2[11], string3[9];
char *string4;
char *string5;
}MyStruct;
Programs prompts user to choose by what field it should sort the data. I am having trouble thinking of a way to sort array effectively. Do I really need to write separate sorting functions for each field? There must be some other way, because writing 8 functions, where 2 would suffice, doesn't look rational.
Look up qsort() from <stdlib.h>. It takes a comparator function. You can write separate comparator functions for the different sort orders, but still use the standard library qsort() to do the sorting.
For example:
int ms_cmp_string1(const void *vp1, const void *vp2)
{
const MyStruct *ms1 = vp1;
const MyStruct *ms2 = vp2;
int cmp = strcmp(ms1->string1, ms1->string2);
if (cmp != 0)
return cmp;
else if (ms1->number1 < ms2->number1)
return -1;
else if (ms1->number1 > ms2->number1)
return +1;
//...other comparisons as required...
else
return 0;
}
This is a decent outline for comparators. This one sorts on string1 and then by number1. You can either write variants that sort on different fields, or devise a scheme that applies the various possible tests in an order of your choosing. But the basic outline works pretty well and is suitable for passing to qsort() without any casts necessary.
You don't need to write 8 functions if only 2 are needed. Build your own qsort function and send a last parameter containing the member offset to the compare function, then, in your compare function, cast pointer + offset to the right type.
Something like:
int comp_int(const void *pa, const void *pb, size_t offset)
{
const int *a = (const int *)((const char *)pa + offset);
const int *b = (const int *)((const char *)pb + offset);
return *a - *b;
}
int comp_string(const void *pa, const void *pb, size_t offset)
{
const char *a = (const char *)pa + offset;
const char *b = (const char *)pb + offset;
return strcmp(a, b);
}
void swap(void *v[], int a, int b)
{
void *temp;
temp = v[a];
v[a] = v[b];
v[b] = temp;
}
void sort(void *v[], int left, int right, size_t offset, int (*comp)(const void *, const void *, size_t))
{
int i, last;
if (left >= right) return;
swap(v, left, (left + right) / 2);
last = left;
for (i = left + 1; i <= right; i++) {
if ((*comp)(v[i], v[left], offset) < 0)
swap(v, ++last, i);
}
swap(v, left, last);
sort(v, left, last - 1, offset, comp);
sort(v, last + 1, right, offset, comp);
}
offsetof can help
Here is a sample of using qsort from my another answer:
struct stringcase { char* string; void (*func)(void); };
void funcB1();
void funcAzA();
struct stringcase cases [] =
{ { "B1", funcB1 }
, { "AzA", funcAzA }
};
struct stringcase work_cases* = NULL;
int work_cases_cnt = 0;
// comparator function
int stringcase_cmp( const void *p1, const void *p2 )
{
return strcasecmp( ((struct stringcase*)p1)->string, ((struct stringcase*)p2)->string);
}
// prepare the data for searching
void prepare() {
// allocate the work_cases and copy cases values from it to work_cases
qsort( cases, i, sizeof( struct stringcase ), stringcase_cmp );
}
If you're using the GNU C library, there's an extension called qsort_r() that lets you pass an extra parameter to the comparison function.
Using some macros:
#include <stdio.h>
#include <stdlib.h>
struct data {
int x, y, z;
};
#define comp(member) comp_##member
#define comp_build(member) \
int comp_##member(const void *pa, const void *pb) \
{ \
const struct data *a = pa, *b = pb; \
return (a->member < b->member) ? -1 : (a->member > b->member); \
}
comp_build(x)
comp_build(y)
comp_build(z)
int main(void)
{
#define ROWS 3
struct data v[] = {
{3, 2, 1},
{1, 3, 2},
{2, 1, 3}
};
int i;
puts("Unsorted");
for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
qsort(v, ROWS, sizeof(struct data), comp(x));
puts("Sorted by x");
for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
puts("Sorted by y");
qsort(v, ROWS, sizeof(struct data), comp(y));
for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
puts("Sorted by z");
qsort(v, ROWS, sizeof(struct data), comp(z));
for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
return 0;
}
My lsearch function should find the value 11 in my array. It does not, and I don't know where the error is. Why does this code not find the value 11?
#include <stdio.h>
#include <string.h>
#define PF printf
int main() {
int intcmp(void *ip1, void * ip2);
void * lsearch(void *key, void *base, int n, int elemSize,
int(* cmpfun)(void *, void *));
int arr[] = {4, 6, 2, 3, 11, 22, 15};
int n = sizeof(arr) / sizeof(int);
int key = 11;
int *found = lsearch(&key, &arr, n, sizeof(int), intcmp);
PF("found=%p", found);
return 1;
}
int intcmp(void *ip1, void * ip2) {
int *p1 = ip1;
int *p2 = ip2;
return *p1 - *p2 == 0;
}
void * lsearch(void *key, void *base, int n, int elemSize,
int(* cmpfun)(void *, void *)) {
int i;
for(i = 0; i < n; i ++) {
void *elemArr = (char *)base + i * elemSize;
if(cmpfun(key, elemArr) == 0)
return elemArr;
}
return NULL;
}
There's a few oddities in your code (PF and the way the functions is declared in main yet defined globally), however, the problem is that your logic if inverted in a two places.
if(cmpfun(key, elemArr) == 0)
return elemArr;
And:
return *p1 - *p2 == 0;
Mentally run through that when the two elements are equal. The == expression will return 1 when the number does actually equal the other. 1 != 0 thus it's not considered found.
Either through a negation in there or just return *p1 - *p2; directly.
I have annotated the code below:
#include <stdio.h>
#include <string.h>
// This is bad practice. It makes your code less readable.
// I won't use it below.
#define PF printf
// Declare this first so a prototype is not needed.
// You violated a C pattern by using `cmp` in the name.
// Comparison functions in C return <0, 0, >0, not a binary value.
// To wit, later you used the comparison correctly. I've fixed the
// inconsistency.
int intcmp(void *vp1, void *vp2)
{ // Most C styles have the brace on its own line, unlike Java. Roll with it.
int *p1 = vp1, *p2 = vp2;
return *p1 - *p2;
}
// Search n elements of size elemSize in the array at
// base in sequence using cmpfun on key,
// to test for equality (cmpfun == 0). Return a pointer
// to the found element or NULL if none.
void *lsearch(void *key, void *base, int n,
int elemSize, int(* cmpfun)(void *, void *))
{
int i;
for (i = 0; i < n; i++) {
void *elemArr = (char*)base + i * elemSize;
if (cmpfun(key, elemArr) == 0)
return elemArr;
}
return NULL;
}
int main()
{
int arr[] = {4, 6, 2, 3, 11, 22, 15};
int n = sizeof(arr) / sizeof(int);
int key = 11;
int *found = lsearch(&key, &arr, n, sizeof(int), intcmp);
printf("found=%p (%d)", found, *(int*)found);
return 0; // By convention zero corresponds to no error.
}