簡體   English   中英

如何檢查C中的變量是否屬於某種類型(比較兩種類型)?

[英]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;

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; \
})

在這里您可以找到標准中的哪些語句以及上述代碼使用哪些 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 *是否指向存儲為intchar的內容。

一種可能的方法是讓變量名稱在變量定義前加上類型信息。

例如:所有整數都有 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM