I want to perform a simple task working with function pointers in C. The task is to get an array (from any type, ie: int / char*), and sum /concatenate each 2 elements in the array. for the char* type, it works fine, but for the int type, the loop seems to jump each 2 elements in the array (and thus overflow the array):
#define N1 4
#define N2 4
typedef void*(*Fn_Sum)(void*, void*);
typedef void(Fn_Prt)(void*);
int sum_num(int a, int b){
return a + b;
}
char* sum_char(char* a, char* b){
char *result = malloc(strlen(a) + strlen(b) + 1);
if (!result) {
printf("ERROR: malloc failed !\n");
return NULL;
}
strcpy(result, a);
strcat(result, b);
return result;
}
void print_num(int a){
printf("%d", a);
}
void print_string(char* a){
int i = 0;
while (a[i] != '\0') {
printf("%c", a[i]);
i++;
}
}
void PrintSums(void** P, int n, Fn_Sum fsum, Fn_Prt fprt){
for(int i = 0; i < n - 1; i++){
(fprt)(fsum(P[i], P[i+1]));
printf(", ");
}
printf("\n");
}
int main() {
int V[N1] = {1,2,3,4};
char* S[N2] = {"a", "d", "c", "d"};
PrintSums(V, N1, sum_num, print_num);
PrintSums(S, N2, sum_char, print_string);
return 0;
}
expected output is: 3, 5, 7, ab, bc, cd, actual outputs: 4, 725939, 4925336, ad, dc, cd,
Create an abstract interface for iterator over the elements. A draft of such interface could look like this:
struct iterator {
...
};
// ptr - a pointer to beginning of the array
// size - size of one element in the array
void it_init(iterator *t, void *ptr, size_t size);
bool it_eq(iterator *t, iterator *o); // compare iteratores
void it_add(iterator *t, size_t n);
void it_inc(iterator *t);
// return a pointer to the element
void *it_get(iterator *t);
Remember to always pass to user callbacks a context variables. Otherwise users will have to use global variables, which make code messy. Create an abstract interface with destructors and constructors of your summing object. Handle errors properly:
// is passed a pointer to user context
// returns 0 on success
typedef int (*Fn_Sum)(void*, void*);
// is passed a pointer to user context
// returns 0 on success
typedef int (Fn_Prt)(void*);
// returns 0 on success
int PrintSums(iterator it, size_t n, Fn_Sum fsum, Fn_Prt fprt, void *sumctx);
After that, implement objects that expose the interface that you want:
struct num { .. };
void num_sum(struct num *t, int el);
void num_print(struct num *t, int el);
// expose interface to PrintSums
// that just calls internal api
int num_PrintSums_Fn_Sum(void *ctx, void *el0) {
struct num *t = ctx;
int *el = el0;
num_sum(t, *el);
return 0;
}
int num_PrintSums_Fn_Prt(void *ctx) {
struct num *t = ctx;
num_print(t);
return 0;
}
An example whole program looks like this:
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
// iterator
typedef struct iterator {
void *ptr;
size_t size;
} iterator;
void it_init(iterator *t, void *ptr, size_t size) {
*t = (iterator){ ptr, size, };
}
// eq is from test(1) shell command. "eq" means "equal"
bool it_eq(iterator *t, iterator *o) {
return t->ptr == o->ptr;
}
void it_add(iterator *t, size_t n) {
t->ptr = (char*)t->ptr + t->size * n;
}
// increment the iterator
void it_inc(iterator *t) {
it_add(t, 1);
}
// return a pointer to the element
void *it_get(iterator *t) {
return t->ptr;
}
// interface
typedef int (*Fn_Sum)(void*, void*);
typedef int (Fn_Prt)(void*);
int PrintSums(iterator it, size_t n, Fn_Sum fsum, Fn_Prt fprt, void *sumctx){
iterator end = it;
it_add(&end, n);
for(; !it_eq(&it, &end); it_inc(&it)) {
int err = fsum(sumctx, it_get(&it));
if (err) return err;
err = fprt(sumctx);
if (err) return err;
printf(", ");
}
printf("\n");
return 0;
}
// num object
struct num {
int sum;
};
void num_init(struct num *t) {
t->sum = 0;
}
void num_sum(struct num *t, int el){
t->sum += el;
}
void num_print(struct num *t){
printf("%d", t->sum);
}
void num_free(struct num *T) {
// nothing, just exists for uniform API
}
// accessors for PrintSums
int num_PrintSums_Fn_Sum(void *ctx, void *el0) {
struct num *t = ctx;
int *el = el0;
num_sum(t, *el);
return 0;
}
int num_PrintSums_Fn_Prt(void *ctx) {
struct num *t = ctx;
num_print(t);
return 0;
}
// string object
struct str {
char *str;
};
void str_init(struct str *t) {
t->str = NULL;
}
int str_sum(struct str *t, const char *str) {
const size_t str_len = t->str == NULL ? 0 : strlen(t->str);
void *p = realloc(t->str, str_len + strlen(str) + 1);
if (p == NULL) {
free(t->str);
t->str = NULL;
return -1;
}
t->str = p;
memcpy(t->str + str_len, str, strlen(str) + 1);
return 0;
}
void str_print(struct str *t) {
if (t->str == NULL) {
printf("(nul)");
} else {
printf("%s", t->str);
}
}
void str_free(struct str *t) {
free(t->str);
}
// interface for PrintSums
int str_PrintSums_Fn_Sum(void *ctx, void *el0) {
struct str *t = ctx;
const char **el = el0;
str_sum(t, *el);
return 0;
}
int str_PrintSums_Fn_Prt(void *ctx) {
struct str *t = ctx;
str_print(t);
return 0;
}
// and finally main
int main() {
int err = 0;
int V[] = {1,2,3,4};
iterator numit;
it_init(&numit, V, sizeof(*V));
struct num numsum; // the object that will hold the sum
num_init(&numsum);
err = PrintSums(numit, sizeof(V)/sizeof(*V), num_PrintSums_Fn_Sum, num_PrintSums_Fn_Prt, &numsum);
if (err) abort();
num_free(&numsum);
char *S[] = {"a", "d", "c", "d"};
iterator strit;
it_init(&strit, S, sizeof(*S));
struct str strsum; // the object that will hold the sum of strings
str_init(&strsum);
err = PrintSums(strit, sizeof(S)/sizeof(*S), str_PrintSums_Fn_Sum, str_PrintSums_Fn_Prt, &strsum);
if (err) abort();
str_free(&strsum); // YES! Remember to pick out the trash
}
and outputs on godbolt :
1, 3, 6, 10,
a, ad, adc, adcd,
The pointers to a constructor and destructor of "sum objects" could be passed to PrintSums
too. That said one could start thinking about creating a virtual table for all these pointers (ie. one struct with function pointers that are needed for PrintSums
...).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.