IT이야기

C에서 변수가 특정 유형(두 유형 비교)인지 어떻게 확인합니까?

cyworld 2021. 10. 14. 21:14
반응형

C에서 변수가 특정 유형(두 유형 비교)인지 어떻게 확인합니까?


C(C++/C# 아님)에서 변수가 특정 유형인지 어떻게 확인합니까?

예를 들면 다음과 같습니다.

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

또는 더 일반적으로: compare(double1,double2)true로 compare(int,double)평가되고 false로 평가 되도록 두 유형을 어떻게 비교합니까? 또한 다른 구성의 구조체도 비교하고 싶습니다.

기본적으로 "struct a" 및 "struct b" 유형의 변수에 대해 작동하는 함수가 있습니다. "struct" 변수로 한 가지 작업을 수행하고 "struct b" 변수로 다른 작업을 수행하고 싶습니다. C는 오버로딩을 지원하지 않고 void포인터가 유형 정보를 잃어버리기 때문에 유형을 확인해야 합니다. BTW, 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를 사용하면 변수의 유형을 알아야 합니다. 모든 함수에는 매개변수에 대한 구체적인 유형이 있으므로(내 생각에 varargs 제외) 함수 본문을 체크인할 필요가 없습니다. 내가 볼 수 있는 유일한 남아 있는 경우는 매크로 본문이고, 글쎄요, C 매크로는 그다지 강력하지 않습니다.

또한 C는 런타임에 유형 정보를 유지하지 않습니다. 이는 가설적으로 유형 비교 확장이 있더라도 컴파일 시간에 유형이 알려진 경우에만 제대로 작동한다는 것을 의미합니다(즉, 두 개의 void *데이터가 동일한 유형의 데이터를 가리키는 지 여부를 테스트하는 것은 작동하지 않음 ).

에 관해서 typeof: 첫째, typeofGCC 확장입니다. C의 표준 부분이 아닙니다. 일반적으로 인수를 한 번만 평가하는 매크로를 작성하는 데 사용됩니다(예: ( GCC 매뉴얼에서 ):

 #define max(a,b) \
   ({ typeof (a) _a = (a); \
      typeof (b) _b = (b); \
     _a > _b ? _a : _b; })

typeof키워드를 사용하면 매크로가 인수 값을 저장하기 위해 로컬 임시를 정의하여 한 번만 평가할 수 있습니다.

간단히 말해서 C는 오버로딩을 지원하지 않습니다. func_a(struct a *)and 를 만들고 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) );
  }
}

물론 이러한 객체를 생성할 때 헤더를 올바르게 초기화하는 것을 기억해야 합니다.


다른 사람들이 이미 말했듯이 이것은 C 언어에서 지원되지 않습니다. 그러나 sizeof()함수를 사용하여 변수의 크기를 확인할 수 있습니다. 이렇게 하면 두 변수가 동일한 유형의 데이터를 저장할 수 있는지 확인하는 데 도움이 될 수 있습니다.

그렇게 하기 전에 아래 주석을 읽으십시오 .


다른 사람들이 언급했듯이 런타임에 변수 유형을 추출할 수 없습니다. 그러나 고유한 "객체"를 구성하고 유형을 함께 저장할 수 있습니다. 그런 다음 런타임에 확인할 수 있습니다.

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;

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

이것은 미친 짓이지만 코드를 사용하면 다음과 같습니다.

fprintf("%x", variable)

컴파일하는 동안 -Wall 플래그를 사용하면 gcc는 인수가 '____' 유형인 동안 'unsigned int' 인수를 예상한다는 경고를 표시합니다. (이 경고가 나타나지 않으면 변수가 'unsigned int' 유형인 것입니다.)

행운을 빕니다!

편집: 아래에서 언급했듯이 이것은 컴파일 시간에만 적용됩니다. 포인터가 작동하지 않는 이유를 알아내려고 할 때 매우 유용하지만 런타임 중에 필요한 경우에는 그다지 유용하지 않습니다.


에서 리눅스 / 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 확장이 사용하는 설명을 찾을 수 있습니다.

(질문이 유형 불일치에 대한 실패에 관한 것이 아니라 어쨌든 여기에 남겨두기 때문에 질문의 범위에 약간 없을 수도 있습니다.)


다른 답변에서 언급했듯이 이제 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


C는 정적으로 유형이 지정된 언어입니다. A형이나 B형에 대해 동작하는 함수는 선언할 수 없고, A형이나 B형을 가지는 변수는 선언할 수 없습니다. 모든 변수에는 명시적으로 선언되고 변경할 수 없는 형이 있으므로, 이 지식을 사용해야 합니다.

그리고 void * 가 float 또는 integer의 메모리 표현을 가리키는지 알고 싶을 때 이 정보를 다른 곳에 저장해야 합니다. 언어는 char * 이 int 또는 char 로 저장된 항목을 가리키는지 여부를 신경 쓰지 않도록 특별히 설계되었습니다 .


이를 위해 간단한 C 프로그램을 작성했습니다... github에 있습니다... GitHub Link

작동 원리는... 먼저 double을 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...

그런 다음 비교를 사용하여 확인할 수 있습니다. 그게 다야...

ReferenceURL : https://stackoverflow.com/questions/6280055/how-do-i-check-if-a-variable-is-of-a-certain-type-compare-two-types-in-c

반응형