【问题标题】:How do I check if a variable is of a certain type (compare two types) in C?如何在 C 中检查变量是否属于某种类型(比较两种类型)?
【发布时间】:2011-09-10 22:11:28
【问题描述】:

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

例如,像这样的:

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

或更笼统地说:我如何比较两种类型,以便compare(double1,double2) 评估为真,compare(int,double) 评估为假。我也想比较不同组成的结构。

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


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

【问题讨论】:

  • 你不能将它们都转换为双精度(并添加0.00)吗?不确定这在 C 中是否可行,只是一个建议。
  • 查看源代码,它在那里声明 doubleVar 是双精度数。不需要(也不可能)在运行时检查它。
  • 回应编辑#1:您是否考虑过使用函数指针(如 vtable)来解决您的问题?
  • 如果你喜欢 sizeof 方法,请阅读that article 了解 gcc 的 tgmath 实现。
  • @Michael Foukarakis 你能举个例子吗?

标签: c types struct


【解决方案1】:

C 不支持这种形式的类型自省。你所问的在 C 中是不可能的(至少没有编译器特定的扩展;但是在 C++ 中是可能的)。

一般来说,使用 C,您应该知道变量的类型。由于每个函数都有其参数的具体类型(我想除了可变参数),你不需要检查函数体。我能看到的唯一剩下的情况是在宏体中,而且,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) );
  }
}

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

【讨论】:

  • 甚至没有一个解决方法或使用宏或其他东西的巧妙技巧?
  • @con-f-use,你为什么需要这个?
  • @con-f-use 宏在编译时工作。当你编写代码时,他们知道的和你一样多。
  • @cnicutar 我知道宏是什么。谢谢! @bdonlan 我几乎不接受你的回答而感到难过。 sizeof 的想法对我来说只是一个更好的解决方法。为你+10,我的朋友,非常感谢。
  • @con-f-use,这是一个很好的解决方法,直到您添加一个成员,大小变得相等,突然它总是采用 struct a 分支,即使它是一个 struct b。 :)
【解决方案2】:

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

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;

【讨论】:

    【解决方案3】:

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

    在此之前,请阅读下面的 cmets

    【讨论】:

    • 你发布了这个,就在我自己想到的时候。一定会尝试的。
    • 补充一点,如果您坚持这样做,请添加一个静态断言以确保大小不会意外变为相同:struct STATIC_ASSERT_size_not_equal_s { char STATIC_ASSERT_size_not_equal[sizeof(a) == sizeof(b) ? -1 : 1]; };
    • 在我比较结构的情况下,它们都有相同的成员,除了一个有两个额外的双成员。因此,如果我独立于架构或其他内容执行“if(sizeof(a)>sizeof(b))”,我应该会被保存。无论如何,谢谢。
    • "你可以使用 sizeof() 函数检查变量的大小" sizeof (int) == sizeof (float) ,但它们的存储格式完全不同。
    【解决方案4】:

    C 是静态类型语言。您不能声明对 A 类型或 B 类型进行操作的函数,也不能声明包含 A 类型或 B 类型的变量。每个变量都有一个明确声明且不可更改的类型,您应该使用这些知识。

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

    【讨论】:

      【解决方案5】:

      到目前为止,在 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)
      

      【讨论】:

        【解决方案6】:

        这太愚蠢了,但如果你使用代码:

        fprintf("%x", variable)
        

        并且您在编译时使用 -Wall 标志,然后 gcc 将发出警告,指出它期望参数为 'unsigned int' 而参数类型为 '____'。 (如果没有出现这个警告,那么你的变量是 'unsigned int' 类型。)

        祝你好运!

        编辑: 正如下面提到的,这仅适用于编译时间。在尝试找出指针不正常运行的原因时非常有用,但如果在运行时需要,则不是很有用。

        【讨论】:

        • 是的,但这在 C 中是不可检查的,并且只能在编译时而不是在运行时工作。
        • 是的。我在尝试调试一些失控的指针数学时遇到了这个问题,因此在编译时识别问题解决了我的问题。
        【解决方案7】:

        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!");
        }
        

        【讨论】:

          【解决方案8】:

          为此,我为此编写了一个简单的 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】:

            来自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 扩展的解释。

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

            【讨论】:

              【解决方案10】:

              正如另一个答案所述,您现在可以在 C11 中使用 _Generic 执行此操作。

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

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

              这也可以用于其他类型,包括结构。例如

              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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2012-10-02
                • 2010-10-31
                • 1970-01-01
                • 2011-03-25
                • 2021-07-04
                • 2017-02-01
                • 1970-01-01
                • 2011-09-28
                相关资源
                最近更新 更多