如何在C中检查变量是否属于某种类型(比较两种类型)?

时间:2011-06-08 14:10:02

标签: c types struct

在C(不是C ++ / C#)中,如何检查变量是否属于某种类型?

例如,像这样:

double doubleVar;
if( typeof(doubleVar) == double ) {
    printf("doubleVar is of type double!");
}

或者更一般:如何比较两种类型,以便compare(double1,double2)评估为true,compare(int,double)评估为false。我也想比较不同构图的结构。

基本上,我有一个函数可以处理“struct a”和“struct b”类型的变量。我想用“struct a”变量做一件事,用“struct b”变量做另一件事。由于C不支持重载并且void指针丢失了它的类型信息,我需要检查类型。顺便说一句,如果你不能比较类型,有typeof运算符的意义是什么?


对于我来说,sizeof方法似乎是一个实用的解决方案。谢谢你的帮助。我仍然觉得它有点奇怪,因为类型在编译时是已知的,但是如果我想象机器中的进程我可以看到,为什么信息不是按类型存储,而是按字节大小存储。除了地址之外,大小是唯一真正相关的东西。

10 个答案:

答案 0 :(得分:35)

到目前为止,获取变量的类型可能在C11中使用_Generic泛型选择。它在编译时工作。

语法有点像switch。这是一个示例(来自this answer):

#define typename(x) _Generic((x),                                                 \
        _Bool: "_Bool",                  unsigned char: "unsigned char",          \
         char: "char",                     signed char: "signed char",            \
    short int: "short int",         unsigned short int: "unsigned short int",     \
          int: "int",                     unsigned int: "unsigned int",           \
     long int: "long int",           unsigned long int: "unsigned long int",      \
long long int: "long long int", unsigned long long int: "unsigned long long int", \
        float: "float",                         double: "double",                 \
  long double: "long double",                   char *: "pointer to char",        \
       void *: "pointer to void",                int *: "pointer to int",         \
      default: "other")

要将它实际用于编译时手动类型检查,您可以使用所需的所有类型定义enum,如下所示:

enum t_typename {
    TYPENAME_BOOL,
    TYPENAME_UNSIGNED_CHAR,
    TYPENAME_CHAR,
    TYPENAME_SIGNED_CHAR,
    TYPENAME_SHORT_INT,
    TYPENAME_UNSIGNED_CHORT_INT,
    TYPENAME_INT,
    /* ... */
    TYPENAME_POINTER_TO_INT,
    TYPENAME_OTHER
};

然后使用_Generic将类型与此enum匹配:

#define typename(x) _Generic((x),                                                       \
        _Bool: TYPENAME_BOOL,           unsigned char: TYPENAME_UNSIGNED_CHAR,          \
         char: TYPENAME_CHAR,             signed char: TYPENAME_SIGNED_CHAR,            \
    short int: TYPENAME_SHORT_INT, unsigned short int: TYPENAME_UNSIGNED_SHORT_INT,     \
          int: TYPENAME_INT,                     \
    /* ... */                                    \
        int *: TYPENAME_POINTER_TO_INT,          \
      default: TYPENAME_OTHER)

答案 1 :(得分:16)

C不支持这种类型的内省。您在C中无法提出的问题(至少没有特定于编译器的扩展;但是,在C ++中可能会这样做。)

通常,对于C,您应该知道变量的类型。由于每个函数都有其参数的具体类型(我认为除了varargs),因此您无需检入函数体。我能看到的唯一剩下的情况是宏体,而且,C宏并不是那么强大。

此外,请注意C不会将任何类型信息保留在运行时中。这意味着,即使假设存在类型比较扩展,它也只能在编译时知道类型时才能正常工作(即,它无法测试两个void *是否指向同一类型数据类型)。

至于typeof:首先,typeof是GCC扩展。它不是C的标准部分。它通常用于编写仅评估其参数一次的宏,例如(来自GCC manual):

 #define max(a,b) \
   ({ typeof (a) _a = (a); \
      typeof (b) _b = (b); \
     _a > _b ? _a : _b; })

typeof关键字允许宏定义一个本地临时值来保存其参数的值,允许它们只被评估一次。

简而言之,C不支持重载;您只需制作func_a(struct a *)func_b(struct b *),然后拨打正确的电话即可。或者,你可以建立自己的内省系统:

struct my_header {
  int type;
};

#define TYPE_A 0
#define TYPE_B 1

struct a {
  struct my_header header;
  /* ... */
};

struct b {
  struct my_header header;
  /* ... */
};

void func_a(struct a *p);
void func_b(struct b *p);

void func_switch(struct my_header *head);
#define func(p) func_switch( &(p)->header )

void func_switch(struct my_header *head) {
  switch (head->type) {
    case TYPE_A: func_a((struct a *)head); break;
    case TYPE_B: func_b((struct b *)head); break;
    default: assert( ("UNREACHABLE", 0) );
  }
}

当然,您必须记住在创建这些对象时正确初始化标题。

答案 2 :(得分:9)

正如其他人已经说过的那样,C语言不支持。但是,您可以使用sizeof()函数检查变量的大小。这可以帮助您确定两个变量是否可以存储相同类型的数据。

在您这样做之前,阅读以下评论

答案 3 :(得分:5)

正如其他人所提到的,您无法在运行时提取变量的类型。但是,您可以构建自己的“对象”并将类型与其一起存储。然后你就可以在运行时检查它了:

typedef struct {
   int  type;     // or this could be an enumeration
   union {
      double d;
      int i;
   } u;
} CheesyObject;

然后在代码中根据需要设置类型:

CheesyObject o;
o.type = 1;  // or better as some define, enum value...
o.u.d = 3.14159;

答案 4 :(得分:5)

Gnu GCC具有内置函数,用于比较类型__builtin_types_compatible_p

https://gcc.gnu.org/onlinedocs/gcc-3.4.5/gcc/Other-Builtins.html

  

如果是非限定版本的,则此内置函数返回1   类型type1和type2(类型,而不是表达式)是   兼容,否则为0。这个内置函数的结果可以是   用于整数常量表达式。

     

此内置函数忽略顶级限定符(例如,const,   易失性)。例如,int等效于const int。

在您的示例中使用:

double doubleVar;
if(__builtin_types_compatible_p(typeof(doubleVar), double)) {
    printf("doubleVar is of type double!");
}

答案 5 :(得分:2)

这是疯狂的愚蠢,但如果你使用代码:

fprintf("%x", variable)

并且在编译时使用-Wall标志,然后gcc将发出一个警告,即当参数类型为'____'时,它需要一个'unsigned int'参数。 (如果没有出现此警告,那么您的变量的类型为'unsigned int'。)

祝你好运!

编辑:如下所示,这仅适用于编译时。在尝试弄清楚为什么你的指针没有表现时非常有用,但如果在运行时需要它们则不是很有用。

答案 6 :(得分:1)

来自linux/typecheck.h

/*
 * Check at compile time that something is of a particular type.
 * Always evaluates to 1 so you may use it easily in comparisons.
 */
#define typecheck(type,x) \
({  type __dummy; \
    typeof(x) __dummy2; \
    (void)(&__dummy == &__dummy2); \
    1; \
})

Here您可以找到解释标准中的哪些语句以及代码使用的GNU扩展名。

(也许有点不在问题的范围内,因为问题不在于类型不匹配的失败,但无论如何,将它留在这里)。

答案 7 :(得分:0)

C是静态类型语言。你不能声明一个操作类型A或类型B的函数,也不能声明包含类型A或类型B的变量。每个变量都有一个显式声明和不可更改的类型,你应该使用这个知识。 / p>

当你想知道 void * 是否指向浮点数或整数的内存表示时 - 你必须将这些信息存储在其他地方。该语言专门设计为不关心 char * 是否指向存储为 int char 的内容。

答案 8 :(得分:0)

为此,我为此写了一个简单的C程序...... 它在github ... GitHub Link

这是怎么回事...... 首先将双精度转换为名为s ..

的char字符串
char s[50];
sprintf(s,"%.2f", yo);

然后使用我的dtype函数来确定类型...... 我的函数将返回一个字符......你可以像这样使用它......

char type=dtype(s);
//Return types are :
//i for integer
//f for float or decimals
//c for character...

然后你可以用比较来检查它...... 就是这样......

答案 9 :(得分:0)

如上所述,您现在可以使用_Generic在C11中执行此操作。

例如,这是一个宏,它将检查某些输入是否与另一种类型兼容:

#include <stdbool.h>
#define isCompatible(x, type) _Generic(x, type: true, default: false)

您可以像这样使用宏:

double doubleVar;
if (isCompatible(doubleVar, double)) {
    printf("doubleVar is of type double!\n");  // prints
}

int intVar;
if (isCompatible(intVar, double)) {
    printf("intVar is compatible with double too!\n");  // doesn't print
}

这也适用于其他类型,包括结构。 E.g。

struct A {
    int x;
    int y;
};

struct B {
    double a;
    double b;
};

int main(void)
{    
    struct A AVar = {4, 2};
    struct B BVar = {4.2, 5.6};

    if (isCompatible(AVar, struct A)) {
        printf("Works on user-defined types!\n");  // prints
    }

    if (isCompatible(BVar, struct A)) {
        printf("And can differentiate between them too!\n");  // doesn't print
    }

    return 0;
}

在typedef上。

typedef char* string;

string greeting = "Hello world!";
if (isCompatible(greeting, string)) {
    printf("Can check typedefs.\n");
}

然而,它并不总能给你你期望的答案。例如,它无法区分数组和指针。

int intArray[] = {4, -9, 42, 3};

if (isCompatible(intArray, int*)) {
    printf("Treats arrays like pointers.\n");
}

// The code below doesn't print, even though you'd think it would
if (isCompatible(intArray, int[4])) {
    printf("But at least this works.\n");
}

从这里借来的答案:http://www.robertgamble.net/2012/01/c11-generic-selections.html