在C(不是C++/C#)中,如何检查变量是否属于某种类型?
例如,像这样:
double doubleVar;
if( typeof(doubleVar) == double ) {
printf("doubleVar is of type double!");
}
Run Code Online (Sandbox Code Playgroud)
或者更一般:我如何比较两种类型,以便compare(double1,double2)评估为true,compare(int,double)并将评估为false.我还想比较不同构图的结构.
基本上,我有一个函数,它对"struct a"和"struct b"类型的变量进行操作.我想用"struct a"变量做一件事,用"struct b"变量做另一件事.由于C不支持重载并且void指针丢失其类型信息,我需要检查类型.顺便说一句,typeof如果你不能比较类型,那么拥有一个算子会有什么意义呢?
对于我来说,sizeof方法似乎是一个实用的解决方案.谢谢你的帮助.我仍然觉得它有点奇怪,因为类型在编译时是已知的,但是如果我想象机器中的进程我可以看到,为什么信息不是按类型存储,而是按字节大小存储.除了地址之外,大小是唯一真正相关的东西.
Min*_*s97 40
到目前为止,获取变量的类型可能在C11中具有_Generic通用选择.它在编译时工作.
语法有点像switch.这是一个样本(来自这个答案):
#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")
Run Code Online (Sandbox Code Playgroud)
要将它实际用于编译时手动类型检查,您可以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
};
Run Code Online (Sandbox Code Playgroud)
然后使用_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)
Run Code Online (Sandbox Code Playgroud)
bdo*_*lan 16
C不支持这种类型的内省.您在C中无法提出的要求(至少没有特定于编译器的扩展;但是在C++中可能会有这种扩展).
通常,对于C,您应该知道变量的类型.由于每个函数都有其参数的具体类型(我认为除了varargs),因此您无需检入函数体.我能看到的唯一剩下的情况是在一个宏体中,而且,C宏并不是那么强大.
此外,请注意C不会将任何类型信息保留到运行时.这意味着,即使假设存在类型比较扩展,它也只能在编译时知道类型时才能正常工作(即,它无法测试两者是否void *指向相同类型的数据).
至于typeof:首先,typeof是GCC扩展.它不是C的标准部分.它通常用于编写仅评估其参数一次的宏,例如(来自GCC手册):
#define max(a,b) \
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
_a > _b ? _a : _b; })
Run Code Online (Sandbox Code Playgroud)
该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) );
}
}
Run Code Online (Sandbox Code Playgroud)
当然,您必须记住在创建这些对象时正确初始化标题.
正如其他人已经说过的那样,C语言不支持.但是,您可以使用该sizeof()函数检查变量的大小.这可以帮助您确定两个变量是否可以存储相同类型的数据.
在此之前,请阅读以下注释.
正如另一个答案提到的,您现在可以在 C11 中使用_Generic.
例如,下面的宏将检查某些输入是否与另一种类型兼容:
#include <stdbool.h>
#define isCompatible(x, type) _Generic(x, type: true, default: false)
Run Code Online (Sandbox Code Playgroud)
您可以像这样使用宏:
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
}
Run Code Online (Sandbox Code Playgroud)
这也可以用于其他类型,包括结构。例如
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;
}
Run Code Online (Sandbox Code Playgroud)
在 typedef 上。
typedef char* string;
string greeting = "Hello world!";
if (isCompatible(greeting, string)) {
printf("Can check typedefs.\n");
}
Run Code Online (Sandbox Code Playgroud)
然而,它并不总能给出您期望的答案。例如,它无法区分数组和指针。
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");
}
Run Code Online (Sandbox Code Playgroud)
从这里借用的答案:http://www.robertgamble.net/2012/01/c11-generic-selections.html
正如其他人所提到的,您无法在运行时提取变量的类型.但是,您可以构建自己的"对象"并将类型与其一起存储.然后你就可以在运行时检查它:
typedef struct {
int type; // or this could be an enumeration
union {
double d;
int i;
} u;
} CheesyObject;
Run Code Online (Sandbox Code Playgroud)
然后在代码中根据需要设置类型:
CheesyObject o;
o.type = 1; // or better as some define, enum value...
o.u.d = 3.14159;
Run Code Online (Sandbox Code Playgroud)
Gnu GCC具有用于比较类型的内置功能__builtin_types_compatible_p。
https://gcc.gnu.org/onlinedocs/gcc-3.4.5/gcc/Other-Builtins.html
如果类型1和类型2(它们是类型,而不是表达式)的非限定版本兼容,则此内置函数返回1,否则返回0。此内置函数的结果可用于整数常量表达式。
此内置函数忽略顶级限定符(例如const,volatile)。例如,int等同于const int。
在您的示例中使用:
double doubleVar;
if(__builtin_types_compatible_p(typeof(doubleVar), double)) {
printf("doubleVar is of type double!");
}
Run Code Online (Sandbox Code Playgroud)
/*
* 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; \
})
Run Code Online (Sandbox Code Playgroud)
在这里,您可以找到对代码使用的标准语句和 GNU 扩展的解释。
(也许有点不在问题的范围内,因为问题不是关于类型不匹配的失败,但无论如何,把它留在这里)。