[英]How do I check if a variable is of a certain type (compare two types) in C?
在 C(不是 C++/C#)中,如何檢查變量是否屬於某種類型?
例如,像這樣:
double doubleVar;
if( typeof(doubleVar) == double ) {
printf("doubleVar is of type double!");
}
或者更籠統:如何比較兩種類型,以便compare(double1,double2)
的計算結果為真,而compare(int,double)
的計算結果為假。 我也想比較不同組成的結構。
基本上,我有一個 function,它對“struct a”和“struct b”類型的變量進行操作。 我想用“struct a”變量做一件事,用“struct b”變量做另一件事。 由於 C 不支持重載並且void
指針丟失了它的類型信息,我需要檢查類型。 順便說一句,如果您不能比較類型,擁有typeof
運算符有什么意義?
sizeof 方法對我來說似乎是一個實用的解決方法。 謝謝你的幫助。 我仍然覺得這有點奇怪,因為類型在編譯時是已知的,但如果我想象我可以看到機器中的進程,為什么信息不是按照類型存儲,而是按照字節大小存儲。 除了地址之外,大小是唯一真正相關的東西。
到目前為止,在 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")
要實際將其用於編譯時手動類型檢查,您可以定義一個包含所有您期望的類型的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)
C 不支持這種形式的類型自省。 您要問的問題在 C 中是不可能的(至少沒有編譯器特定的擴展;但是在 C++ 中是可能的)。
通常,使用 C 您應該知道變量的類型。 由於每個 function 都有其參數的具體類型(我想除了可變參數),您不需要檢查 function 主體。 我能看到的唯一剩下的情況是在宏體中,而且,C 宏並不是真的那么強大。
此外,請注意 C 不會將任何類型信息保留到運行時。 這意味着,即使假設存在類型比較擴展,它也只有在編譯時已知類型時才能正常工作(即,無法測試兩個void *
是否指向相同類型的數據)。
至於typeof
:首先, typeof
是 GCC 擴展。 它不是 C 的標准部件。 它通常用於編寫僅評估其 arguments 一次的宏,例如(來自GCC 手冊):
#define max(a,b) \
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
_a > _b ? _a : _b; })
typeof
關鍵字讓宏定義一個本地臨時變量來保存它的 arguments 的值,允許它們只被評估一次。
總之,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) );
}
}
當然,您必須記住在創建這些對象時正確初始化 header。
正如其他人已經說過的,C 語言不支持此功能。 但是,您可以使用sizeof()
function 檢查變量的大小。 這可以幫助您確定兩個變量是否可以存儲相同類型的數據。
在你這樣做之前,請閱讀下面的評論。
Gnu GCC 有一個內置的 function 用於比較類型__builtin_types_compatible_p
。
https://gcc.gnu.org/onlinedocs/gcc-3.4.5/gcc/Other-Builtins.html
如果類型 type1 和 type2(它們是類型,不是表達式)的非限定版本兼容,則此內置 function 返回 1,否則返回 0。 此內置 function 的結果可用於 integer 常量表達式。
這個內置的 function 忽略了頂級限定符(例如,const、volatile)。 例如,int 等價於 const int。
在您的示例中使用:
double doubleVar;
if(__builtin_types_compatible_p(typeof(doubleVar), double)) {
printf("doubleVar is of type double!");
}
正如其他人所提到的,您無法在運行時提取變量的類型。 但是,您可以構建自己的“對象”並將類型與它一起存儲。 然后你就可以在運行時檢查它:
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;
/*
* 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; \
})
在這里您可以找到標准中的哪些語句以及上述代碼使用哪些 GNU 擴展的解釋。
(也許有點不在問題的 scope 中,因為問題不是關於類型不匹配的失敗,但無論如何,把它留在這里)。
正如提到的另一個答案,您現在可以在 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
這非常愚蠢,但是如果您使用以下代碼:
fprintf("%x", variable)
並且您在編譯時使用 -Wall 標志,然后 gcc 將發出警告,指出它需要一個“unsigned int”參數,而參數類型為“____”。 (如果沒有出現這個警告,那么你的變量是'unsigned int'類型。)
祝你好運!
編輯:正如下面提到的,這僅適用於編譯時間。 在試圖弄清楚為什么你的指針沒有表現時非常有用,但如果在運行時需要,它就不是很有用。
為此,我為此編寫了一個簡單的 C 程序...它位於 github... GitHub 鏈接
這里它是如何工作的......首先將你的雙精度轉換為一個名為 s.. 的字符字符串。
char s[50];
sprintf(s,"%.2f", yo);
然后使用我的dtype
function 來確定類型...我的 function 將返回單個字符...您可以這樣使用它...
char type=dtype(s);
//Return types are :
//i for integer
//f for float or decimals
//c for character...
然后你可以用比較來檢查它......就是這樣......
C 是靜態類型語言。 您不能聲明對 A 類型或 B 類型進行操作的 function,也不能聲明包含 A 類型或 B 類型的變量。每個變量都有明確聲明且不可更改的類型,您應該使用這些知識。
當您想知道void *是否指向浮點數的 memory 或 integer 表示時 - 您必須將此信息存儲在其他地方。 該語言專門設計為不關心char *是否指向存儲為int或char的內容。
一種可能的方法是讓變量名稱在變量定義前加上類型信息。
例如:所有整數都有 i_ 所有浮點數都有 f_ 等等。
變量名可以通過#<variable_name>得到,這個
GCC中有一個內置的function。
內置 Function: int __builtin_types_compatible_p (type1, type2)
可以使用內置的 function __builtin_types_compatible_p
來判斷兩個類型是否相同。
我已經搜索了一段時間來解決控制數據類型的問題,我認為也許我的成立可以很好地滿足最初的需求@con-f-use,即使它不是完全相同的問題。
另一種控制數據類型的方法可以使用具有預定義類型的聯合來完成。 就我而言,我有一個定義的結構,我最初使用 void* 來允許傳遞潛水員數據類型:最初:
//[main]:
uint32_t vtest3= 100000;
int32_t vtest2= 100000;
struct entity list[] = {
{ TYPE_INT32, s_int32_t, .label="tension", &vtest3},
{ TYPE_INT32, s_int32_t, .label="tension", &vtest3}
};
//[file.h]:
struct entity {
enum entity_type type;
uint32_t dimension;
char* label;
void* ptr_data;
uint32_t offset;
};
enum entity_type {
TYPE_NONE = 0,
TYPE_INT8 = 1,
TYPE_INT16 = 2,
TYPE_INT32 = 3,
TYPE_INT64 = 4,
TYPE_UINT8 = 5,
TYPE_UINT16 = 6,
TYPE_UINT32 = 7,
TYPE_UINT64 = 8,
TYPE_FLOAT32 = 9
};
這種方法的問題在於它以不受控制的方式接受所有類型的變量。 沒有簡單的方法來控制 void* 指針引用的數據類型,例外可能認為使用宏和 _Generic,如本線程之前所述。
如果程序員決定傳遞與接受類型列表不同的類型,則編譯時不會拋出錯誤。 . 他們的另一種方法是用聯合替換 void*,這樣結構雖然只接受聯合列表中定義的特定數據類型。 如果程序員決定傳遞一個尚未在 ptr_data union{...} 中定義的類型的指針,它將引發錯誤。
//[file.h]:
enum entity_type {
TYPE_NONE = 0,
TYPE_INT8 = 1,
TYPE_INT16 = 2,
TYPE_INT32 = 3,
TYPE_INT64 = 4,
TYPE_UINT8 = 5,
TYPE_UINT16 = 6,
TYPE_UINT32 = 7,
TYPE_UINT64 = 8,
TYPE_FLOAT32 = 9
};
struct entity {
enum entity_type type;
uint32_t dimension;
char* label;
union {
uint8_t *uint8;
uint16_t *uint16;
uint32_t *uint32;
uint32_t *uint;
int16_t *int16;
int32_t *int32;
int64_t *int64;
float *f;
} ptr_data;
uint32_t offset;
};
[main:]
uint32_t vtest3= 100000;
int32_t vtest2= 100000;
struct entity list[] = {
{ TYPE_INT32, s_int32_t, .label="a", .ptr_data = {.uint16=&vtest1}
},
{ TYPE_INT32, s_int32_t, .label="b", .ptr_data = {.int32=&vtest2}
};
該方法利用聯合隱式控制程序員在結構中插入的變量的數據類型。 如果在編譯時拋出錯誤時不更正編譯器。
顯然,這個代碼示例遠非完美,不能直接使用,但我試圖以盡可能清晰的方式解釋我提出的邏輯和想法;)
從 C2x 開始, typeof
現在是該語言標准的一部分。 這允許創建一個宏來比較兩個值的類型:
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#define sametypeof(A,B) _Generic(A, typeof(B): true, default: false)
int main() {
if (sametypeof(1, 2)) {
printf("1 and 2 have the same type.\n");
} else {
printf("1 and 2 don't have the same type.\n");
}
}
(此編譯使用 GCC 13 的最新實驗版本,使用 -std=c2x 標志)
如果要比較兩種類型,可以使用以下解決方法:
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#define sametype(A,B) _Generic(*((A*)0), B: true, default: false)
int main() {
if (sametype(void*, nullptr_t)) {
printf("void* and nullptr_t are the same type.\n");
} else {
printf("void* and nullptr_t are not the same type.\n");
}
}
盡管*((A*)0)
在運行時不是有效代碼,但編譯器仍然能夠將其類型推斷為A
,因此它將在_Generic
中工作,因為代碼本身不會運行並將被丟棄。 (據我所知,這個技巧在我使用過的每個 C11 兼容編譯器中都有效,包括 Clang 和 Tiny C 編譯器)
(你也不能只做(A)0
因為 0 不能轉換為結構)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.