为函数/方法返回void *

时间:2019-01-27 18:28:26

标签: c

在C语言中是否有一个函数可以返回“动态”返回类型

示例

printResult (NumOrChar());

void* NumOrChar(void) {
   // return int  or char 
}

void printResult (void* input) {
  if (isdigit(input)) {
     printf("It's a number");
}
  else {
     printf("It's not a number");
}

4 个答案:

答案 0 :(得分:2)

在某些情况下,您可以使用_Generic

int func_int(int *p)
{
    printf("%s\n", __FUNCTION__);
    return 5;  /* does not make too much sense */
}

float func_float(float *p)
{
    printf("%s\n", __FUNCTION__);
    return 5.0f;  /* does not make too much sense */
}

double func_double(double *p)
{
    printf("%s\n", __FUNCTION__);
    return 5.0;  /* does not make too much sense */
}


#define func(p) _Generic((p), \
              int *: func_int, \
              float *: func_float, \
              double *: func_double)(p) \

答案 1 :(得分:2)

函数肯定可以返回void *。但这是一种特定的指针类型,其属性使其适合于将指针传递给任何类型的对象。这是 不是 的通用通配符类型。而且,它不携带有关它指向的对象的实际类型(如果有)的任何类型的信息,因此无法动态确定该类型。 C ++程序员可能将这种情况描述为C不提供任何RTTI。

相反,您可以返回一种类型,该类型可以传达预先已知的各种类型的对象,并带有一种区分这些对象的机制。例如,

union num_or_string {
    struct { _Bool is_num; };
    struct { _Bool _x1; int num; };
    struct { _Bool _x2; char *string; };
};

union num_or_string NumOrChar(void) {
    // return a union num_or_string containing an int or a char *
}

void printResult (union num_or_string) {
    if (num_or_string.is_num) {
        printf("It's a number: %d\n", num_or_string.num);
    } else {
        printf("It's a string: %s\n", num_or_string.string);
    }
}

答案 2 :(得分:0)

可以将函数指针的结构用作虚拟表,而不是包含ifs块,包括$doc = new DOMDocument(); $doc->loadHTML($shtm); $div = $doc->getElementById("a"); echo 'div[id="a"] contains ', $div->textContent; 。以下将动态创建to_string,可以是TypeNum

Letter

可能会对您的功能造成过大杀伤力,但是随着类型的增加,这变得越来越有吸引力。可以考虑使用#include <stddef.h> /* offsetof */ #include <stdio.h> /* [|s|sn]printf, fgets, stdin */ #include <stdlib.h> /* malloc, free, strtol */ #include <ctype.h> /* isdigit */ #include <errno.h> #include <assert.h> struct Type; typedef void (*TypeToString)(const struct Type *const, char (*const)[32]); typedef void (*TypeAction)(struct Type *const); struct Type { const struct TypeVt *vt; }; /* Num extends Type. */ struct Num { struct Type base; int value; }; static struct Num *num_upcast(struct Type *const type) { return (struct Num *)(void *)((char *)type - offsetof(struct Num, base)); } static const struct Num *const_num_upcast(const struct Type *const type) { return (const struct Num *)(const void *)((const char *)type - offsetof(struct Num, base)); } static void num_to_string(const struct Type *const type, char (*const a)[32]) { const struct Num *const num = const_num_upcast(type); snprintf(*a, sizeof *a, "%d", num->value); /* C99. */ } static void num_delete(struct Type *const type) { struct Num *const num = num_upcast(type); free(num); } /* Letter extends Type. */ struct Letter { struct Type base; char letter; }; static struct Letter *letter_upcast(struct Type *const type) { return (struct Letter *)(void *)((char *)type - offsetof(struct Letter, base)); } static const struct Letter *const_letter_upcast(const struct Type *const type) { return (const struct Letter *)(const void *)((const char *)type - offsetof(struct Letter, base)); } static void letter_to_string(const struct Type *const t, char (*const a)[32]) { const struct Letter *const letter = const_letter_upcast(t); sprintf(*a, "%c", letter->letter); } static void letter_delete(struct Type *const type) { struct Letter *const letter = letter_upcast(type); free(letter); } static const struct TypeVt { const char *name; const TypeToString to_string; const TypeAction delete; } num_vt = { "num", &num_to_string, &num_delete }, letter_vt = { "char", &letter_to_string, &letter_delete }; static void type_to_string(const struct Type *const t, char (*const a)[32]) { assert(t); t->vt->to_string(t, a); } static void type_delete(struct Type *const t) { assert(t); t->vt->delete(t); } static struct Type *num(const int value) { struct Num *num = malloc(sizeof *num); if(!num) return 0; num->base.vt = &num_vt; num->value = value; return &num->base; } static struct Type *letter(const char letter) { struct Letter *l = malloc(sizeof *l); if(!l) return 0; l->base.vt = &letter_vt; l->letter = letter; return &l->base; } static struct Type *read_type(void) { struct Type *type; char buffer[64]; if(!fgets(buffer, sizeof buffer, stdin)) return 0; if(isdigit(buffer[0])) { long n; errno = 0; n = strtol(buffer, 0, 0); if(errno) return 0; type = num(n); } else { type = letter(buffer[0]); } return type; } int main(void) { char a[32]; struct Type *type = 0; int is_success = 0; do { if(!(type = read_type())) break; type_to_string(type, &a); printf("\"%s\" is of type %s.\n", a, type->vt->name); is_success = 1; } while(0); { if(type) type_delete(type); } if(!is_success) return perror("Failure"), EXIT_FAILURE; return EXIT_SUCCESS; } 相似的间隔类型,以便可以将其完全分配在堆栈上。

union

答案 3 :(得分:-2)

我想您是在谈论 C#功能(根据我的Google search)。

在C语言中,除非自己完成,否则是不可能的(其他答案显示了示例)。根据您的需要,它可以容易也可以困难。如果您确实需要这样做,则应该考虑切换到另一种语言(在他们中有时将它们称为variants)。