web-dev-qa-db-ja.com

Cで変数が特定の型(2つの型を比較)であるかどうかを確認するにはどうすればよいですか?

Cでは(C++/C#ではなく)変数が特定の型であるかどうかを確認するにはどうすればよいですか?

たとえば、次のようなもの:

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

またはより一般的:compare(double1,double2)がtrueに評価され、compare(int,double)がfalseに評価されるように2つのタイプを比較するにはどうすればよいですか。また、異なる構成の構造体も比較したいと思います。

基本的に、タイプ "struct a"および "struct b"の変数を操作する関数があります。 「struct a」変数を使用して1つのことを行い、「struct b」変数を使用してもう1つを行います。 Cはオーバーロードをサポートしていないため、voidポインターは型情報を失うため、型を確認する必要があります。ところで、型を比較できない場合、typeof演算子を使用する意味は何でしょうか?


Sizeofメソッドは、私にとって実用的な回避策のようです。ご協力いただきありがとうございます。コンパイル時に型がわかっているため、少し奇妙ですが、マシンのプロセスを想像すると、なぜ情報は型の観点ではなく、バイトサイズの観点で保存されているのでしょうか。アドレス以外に本当に関連するのはサイズだけです。

52
con-f-use

現在、変数の型を取得することは、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)
36
Mints97

Cは、この形式の型内観をサポートしていません。あなたが求めていることはCでは不可能です(少なくともコンパイラ固有の拡張機能がなければ、C++では可能です)。

一般に、Cを使用すると、変数の型を知っていることが期待されます。すべての関数にはそのパラメーターに具体的な型があるため(可変引数を除く、と思われます)、関数本体をチェックインする必要はありません。私が見ることができる唯一の残りのケースはマクロ本体にあります、そして、Cマクロは本当にそれほど強力ではありません。

さらに、Cはランタイムに型情報を保持しないことに注意してください。これは、仮に、型比較拡張機能があったとしても、コンパイル時に型がわかっている場合にのみ適切に機能することを意味します(つまり、2つの_void *_が同じものを指すかどうかをテストすることはできません)データのタイプ)。

typeofに関して:最初に、typeofはGCC拡張です。 Cの標準部分ではありません。通常、引数を1回だけ評価するマクロを記述するために使用されます。たとえば、( GCCマニュアル から):

_ #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) );
  }
}
_

もちろん、これらのオブジェクトを作成するときは、ヘッダーを適切に初期化する必要があります。

16
bdonlan

他の人がすでに言っているように、これはC言語ではサポートされていません。ただし、sizeof()関数を使用して変数のサイズを確認できます。これは、2つの変数が同じタイプのデータを格納できるかどうかを判断するのに役立ちます。

それを行う前に、以下のコメントを読んでください

9
Steve Walsh

Gnu GCCには、型を比較す​​るための組み込み関数__builtin_types_compatible_p

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

この組み込み関数は、型type1とtype2(式ではなく型)の非修飾バージョンに互換性がある場合は1を返し、そうでない場合は0を返します。この組み込み関数の結果は、整数定数式で使用できます。

この組み込み関数は、最上位修飾子(const、volatileなど)を無視します。たとえば、intはconst intと同等です。

あなたの例で使用:

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

他の人が述べたように、実行時に変数の型を抽出することはできません。ただし、独自の「オブジェクト」を作成し、それとともに型を保存することもできます。次に、実行時にそれを確認できます。

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;
5
Mark Wilkins

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=.

(質問は型の不一致による失敗ではなく、とにかくここに残しておくので、質問の範囲には少し入っていないかもしれません)。

2
Green Tree

これはばかげているが、コードを使用する場合:

fprintf("%x", variable)

コンパイル時に-Wallフラグを使用すると、gccは、引数が「____」型であるときに「unsigned int」の引数が必要であることを警告します。 (この警告が表示されない場合、変数のタイプは「unsigned int」です。)

幸運を祈ります!

編集:以下で取り上げたように、これはコンパイル時間にのみ適用されます。ポインターが動作しない理由を理解しようとする場合は非常に役立ちますが、実行中に必要な場合はあまり役に立ちません。

2
Daniel Peirano

別の回答として、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;
}

そしてtypedefsについて。

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

1
Saeed Baig

Cは静的に型付けされた言語です。タイプAまたはタイプBを操作する関数を宣言することはできません。また、タイプAまたはタイプBを保持する変数を宣言することはできません。すべての変数には明示的に宣言された変更不可能なタイプがあり、この知識を使用することになっています。

void *が浮動小数点または整数のメモリ表現を指しているかどうかを知りたい場合は、この情報を別の場所に保存する必要があります。この言語は、char *intまたはcharとして保存されているものを指しているかどうかを気にしないように特別に設計されています。

0
blaze

そのために、そのための簡単なCプログラムを作成しました... githubにあります... GitHub Link

ここでの仕組み...まず、doubleをs。という名前の文字列に変換します。

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

次に、私のdtype関数を使用して型を決定します...私の関数は1文字を返します...次のように使用できます...

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

次に、比較を使用して確認できます...それだけです...

0
Subham Debnath