programing

C의 형식 안전성

subpage 2023. 8. 2. 09:03
반응형

C의 형식 안전성

C가 유형을 조금 더 인지하고 유형 안전을 보장할 수 있는 방법이 있습니까?
고려 사항:

typedef unsigned cent_t;
typedef unsigned dollar_t;

#define DOLLAR_2_CENT(dollar)       ((cent_t)(100*(dollar)))

void calc(cent_t amount) {
    // expecting 'amount' to semantically represents cents...
}

int main(int argc, char* argv[]) {
    dollar_t amount = 50;
    calc(DOLLAR_2_CENT(amount));  // ok
    calc(amount);                 // raise warning
    return 0;
}

위의 코드를 최소한 gcc로 경고를 올리는 방법이 있습니까?
C-structs를 사용하여 포장할 수 있습니다.unsigneds 그리고 원하는 결과를 얻기 위해, 나는 단지 그것을 하는 더 우아한 방법이 있는지 궁금했을 뿐입니다.
그것보다 조금 더 많을 수 있습니까?

문제는 C가 두 유형의 def를 모두 유형이기 때문에 구별되는 유형으로 취급하지 않는다는 것입니다.unsigned.

이것을 피하기 위한 다양한 속임수가 있습니다.한 가지는 당신의 유형을 열거형으로 바꾸는 것입니다.좋은 컴파일러는 특정 열거형에서 다른 유형으로 암시적 변환할 때 강력한 타이핑 경고를 적용합니다.

좋은 컴파일러가 없더라도 열거형을 사용하면 다음과 같은 작업을 수행할 수 있습니다.

typedef enum { FOO_CENT  } cent_t;
typedef enum { FOO_DOLLAR} dollar_t;

#define DOLLAR_2_CENT(dollar)       ((cent_t)(100*(dollar)))

void calc(cent_t amount) {
    // expecting 'amount' to semantically represents cents...
}

#define type_safe_calc(amount) _Generic(amount, cent_t: calc(amount))

int main(int argc, char* argv[]) {
    dollar_t amount = 50;
    type_safe_calc(DOLLAR_2_CENT(amount));  // ok
    type_safe_calc(amount);         // raise warning

    return 0;
}

더 일반적인/전통적인 방법은 일반 구조 래퍼를 사용하는 것입니다. 여기서 "티켓" 열거형을 사용하여 유형을 표시합니다.예:

typedef struct
{
  type_t type;
  void*  data;
} wrapper_t;

...

cent_t my_2_cents;
wrapper_t wrapper = {CENT_T, &my_2_cents};

...

switch(wrapper.type)
{
  case CENT_T: calc(wrapper.data)
  ...
}

장점은 모든 C 버전에서 작동한다는 것입니다.단점은 코드와 메모리 오버헤드이며 런타임 검사만 허용한다는 것입니다.

별칭 지정은 C에서 매우 구체적인 좁은 의미를 가지고 있으며, 그것은 당신이 생각하는 것이 아닙니다."typedefing"이라고 말할 수 있습니다.

그리고 대답은 '아니오'입니다. 할 수 없습니다.어쨌든 우아한 방식은 아닙니다.각 숫자 유형에 대해 구조를 사용하고, 각 숫자 유형에 대해 별도의 함수 집합을 사용하여 산술 연산을 수행할 수 있습니다.곱셈에 관한 한, 당신은 운이 없습니다.피트에 파운드를 곱하기 위해서는 세 번째 유형이 필요합니다.또한 발 제곱, 발 제곱, 초에서 마이너스 2의 거듭제곱, 그리고 무한한 수의 다른 유형도 필요합니다.

만약 이것이 당신이 추구하는 것이라면, C는 올바른 언어가 아닙니다.

이를 위해 빌드 프로세스에서 정적 분석 도구를 사용해야 합니다.

예를 들어 코드에서 PCLint를 실행하면 다음과 같은 출력이 제공됩니다.

  [Warning 632] Assignment to strong type 'cent_t' in context: arg. no. 1
  [Warning 633] Assignment from a strong type 'dollar_t' in context: arg. no. 1

http://www.gimpel.com/html/strong.htm

편집: 여기 컴파일러가 지원하지 않는 경우를 대비하여 C89에서도 작동하는 대안이 있습니다._Generic실렉터(많은 컴파일러가 그렇지 않고 종종 컴퓨터에 설치된 것에 집착합니다.)

매크로를 사용하여 의 사용을 단순화할 수 있습니다.struct

#define NEWTYPE(nty,oty) typedef struct { oty v; } nty
#define FROM_NT(ntv)       ((ntv).v)
#define TO_NT(nty,val)     ((nty){(val)})  /* or better ((nty){ .v=(val)}) if C99 */


NEWTYPE(cent_t, unsigned);
NEWTYPE(dollar_t, unsigned);

#define DOLLAR_2_CENT(dollar)       (TO_NT(cent_t, 100*FROM_NT(dollar)))

void calc(cent_t amount) {
     // expecting 'amount' to semantically represents cents...
}  

int main(int argc, char* argv[]) {
    dollar_t amount = TO_NT(dollar_t, 50);  // or alternatively {50};
    calc(DOLLAR_2_CENT(amount));  // ok
    calc(amount);                 // raise warning
    return 0;
}

당신은 경고보다 더 강해집니다.다음은 gcc 5.1을 사용한 컴파일 결과입니다.

gcc - O3 - 벽 편집 1.cEdit1.c: '주' 기능:
Edit1.c:17:10: 오류: 'calc'의 인수 1에 대해 호환되지 않는 유형calc(금액); // 경고를 올립니다.^편집 1.c:10:6: 참고: 'cent_t {akastruct}'이(가) 필요하지만 인수 유형이 'dollar_t {akastruct}'입니다.void calc(cent_t 금액);// {

그리고 여기 gcc 3.4의 결과가 있습니다.

gcc - O3 - 벽 편집 1.cEdit1.c: '주' 기능:
Edit1.c:17: 오류: 'calc'의 인수 1에 대해 호환되지 않는 유형

언급URL : https://stackoverflow.com/questions/36351496/type-safety-in-c

반응형