簡體   English   中英

如何使用 C 中的 function 指針遍歷泛型類型(void**)的數組?

[英]how can I iterate through an array of generic type (void**) in with function pointer in C?

我想使用 C 中的 function 指針執行一個簡單的任務。 任務是獲取一個數組(來自任何類型,即:int / char*),並對數組中的每兩個元素求和/連接。 對於 char* 類型,它可以正常工作,但對於 int 類型,循環似乎會跳過數組中的每 2 個元素(從而溢出數組):

#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;
}

預期 output 為:3、5、7、ab、bc、cd,實際輸出:4、725939、4925336、ad、dc、cd、

為元素上的迭代器創建一個抽象接口。 此類界面的草稿可能如下所示:

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);

請記住始終將上下文變量傳遞給用戶回調。 否則用戶將不得不使用全局變量,這會使代碼變得混亂。 使用求和 object 的析構函數和構造函數創建一個抽象接口。 正確處理錯誤:

// 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);

之后,實現公開所需接口的對象:

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;
}

整個程序的示例如下所示:

#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

}

Godbolt 上的輸出

1, 3, 6, 10, 
a, ad, adc, adcd, 

指向“sum objects”的構造函數和析構函數的指針也可以傳遞給PrintSums 也就是說,人們可以開始考慮為所有這些指針創建一個虛擬表(即一個帶有 function 指針的結構,這是PrintSums所需的......)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM