source

C++ FAQ의 안전하지 않은 매크로에 대한 설명?

lovecheck 2023. 9. 24. 12:58
반응형

C++ FAQ의 안전하지 않은 매크로에 대한 설명?

C++ FAQ에 따르면 매크로는 사악합니다.

[9.5] 일반 기존 #정의 매크로 대신 인라인 기능을 사용해야 하는 이유는 무엇입니까?

왜냐면#define매크로는 4가지 다른 방식으로 악합니다: 악 #1, 악 #2, 악 #3, 악 #4.가끔은 사용해야 할 때도 있지만 여전히 사악합니다.와는 달리#define매크로, 인라인 함수는 항상 모든 인수를 한 번만 정확하게 평가하기 때문에 악명 높은 매크로 오류를 방지합니다.즉, 인라인 함수를 호출하는 것은 의미론적으로 일반 함수를 호출하는 것과 같으며, 단지 더 빠르게 호출할 뿐입니다.

// A macro that returns the absolute value of i
#define unsafe(i)  \
        ( (i) >= 0 ? (i) : -(i) )

// An inline function that returns the absolute value of i
inline
int safe(int i)
{
  return i >= 0 ? i : -i;
}

int f();

void userCode(int x)
{
  int ans;

  ans = unsafe(x++);   // Error! x is incremented twice
  ans = unsafe(f());   // Danger! f() is called twice

  ans = safe(x++);     // Correct! x is incremented once
  ans = safe(f());     // Correct! f() is called once
}

또한 매크로와 달리 인수 유형을 확인하고 필요한 변환을 올바르게 수행합니다.

매크로는 건강에 해로우므로 꼭 사용해야 하는 경우가 아니면 사용하지 마십시오.

누가 그 이유를 설명해 줄 수 있습니까?unsafe(x++)점증적으로x두 번이요? 저는 알 수가 없습니다.

전처리기를 통해 실행하면 문제가 나타납니다.사용.gcc -E(사용 가능)cpp -P, 어디서-P옵션을 사용하면 생성되지 않습니다.#선),

inline
int safe(int i)
{
  return i >= 0 ? i : -i;
}

int f();

void userCode(int x)
{
  int ans;

  //    increment 1      increment 2 (one of these)
  //        |             |     |
  //        V             V     V
  ans = ( (x++) >= 0 ? (x++) : -(x++) );
  ans = ( (f()) >= 0 ? (f()) : -(f()) );

  ans = safe(x++);
  ans = safe(f());
}

예술적이지 않은 소음 노트로서, 그 기능은f()또한 두번이나.unsafe매크로. 아마도 순수한 것일 도 있고(부작용이 없기 때문에) 틀린 것은 아닙니다.하지만 여전히 최선은 아닙니다.

따라서 인라인 함수는 다른 기본 요소인 변수 및 표현식과 동일한 의미 수준에서 작동하기 때문에 함수와 같은 매크로보다 일반적으로 안전합니다. 그리고 매니페스트 상수의 경우,enums는 종종 더 깔끔할 수 있습니다. 매크로의 좋은 용도는 무엇입니까?

컴파일 시에만 알 수 있는 상수를 설정합니다.컴파일할 때 명령줄에서 매크로를 정의할 수 있습니다.대신에

#define X 12

소스 파일에서 추가할 수 있습니다.

-DX=12

에게cc지휘.할 수도 있습니다.#undef X와의 지휘계통에서-UX.

이것은 조건부 컴파일과 같은 것들을 허용합니다.

#if X
   do this;
#else
   do that;
#endif
   while (loop);

makefile에 의해 제어될 수 있고, 그 자체는 구성 스크립트로 생성될 수 있습니다.

엑스 마크로스.X-Macros, IMO의 가장 강력한 용도는 다음과 같습니다.enum문자열을 인쇄할 수 있는 식별자입니다.처음에는 우습게 보이지만 이러한 종류의 병렬 정의를 통해 중복 및 동기화 문제를 줄일 수 있습니다.

#define NAMES(_) _(Alice) _(Bob) _(Caravaggio) _(DuncanIdaho)
#define BARE(_) _ ,
#define STRG(_) #_ ,
enum { NAMES(BARE) };
char *names[] = { NAMES(STRG) };

매크로의 이름을 인수로 다른 매크로에 전달한 다음 인수를 매크로인 처럼 사용하여 전달된 매크로를 호출할 수 있습니다.X-Macros에 대한 자세한 내용은 이 질문을 참조하십시오.

매크로는 프로그램이 컴파일되기 에 효과적으로 복사/붙여넣기를 수행합니다.

unsafe(x++)

될 것입니다.

( (x++) >= 0 ? (x++) : -(x++) )

전처리기는 컴파일 전에 매크로를 바꿉니다.

컴파일러는 다음과 같이 봅니다.

  ( (x++) >= 0 ? (x++) : -(x++) )

unsafe(x)합니다를 합니다.x두 번. 한 번은 진실 값을 결정하고, 두 번째는 3원 연산자의 두 갈래 중 하나입니다.nsafe는 평가된 인수를 받습니다. 식은 함수 호출 전에 한 번 평가되며 함수 호출은 로컬 변수와 함께 작동합니다.

unsafe위험할 정도로 위험하지는 않습니다 하는 것과 또는 을 평가하는 것 합니다.3차 연산자는 검정을 평가하는 것과 결과식 또는 대체식을 평가하는 것 사이의 순서점을 도입합니다.unsafe(x++)의지가 확실하게 증가합니다.x두번, 물론 문제는 이러한 행동이 예상치 못했다는 것입니다.일반적으로 식을 두 번 이상 확장하는 매크로에는 이러한 보장이 없습니다.보통, 그들은 명확하지 않은 행동을 합니다!

1999년경 나는 부작용이 있는 매크로의 사용을 잡기 위한 라이브러리 모듈을 제작했습니다.

따라서 "악" 매크로를 작성하여 사용하면 기계가 부작용이 있는 인수와 함께 실수로 사용되는 상황을 파악할 수 있습니다(런타임에 해당 사용을 처리할 수 있는 적절한 코드 범위가 있다면).

인 가 있습니다.unsafe.c 를(를) 포함하고 .sfx.h그리고 a를 사용합니다.SFX_CHECK 토큰 unsafe:

#include "sfx.h"

#define unsafe(i)  \
          ( (SFX_CHECK(i)) >= 0 ? (i) : -(i) )

inline
int safe(int i)
{
  return i >= 0 ? i : -i;
}

int f(void)
{
  return 0;
}

int main(void)
{
  int ans;
  int x = 0;

  ans = unsafe(x++);   // Error! x is incremented twice
  ans = unsafe(f());   // Danger! f() is called twice

  ans = safe(x++);     // Correct! x is incremented once
  ans = safe(f());     // Correct! f() is called once
}

우리는 모든 것을 컴파일하고 리눅스 셸 프롬프트에서 실행합니다.

$ gcc unsafe.c hash.c except.c sfx.c -o unsafe
$ ./unsafe
unsafe.c:22: expression "x++" has side effects
unsafe.c:23: expression "f()" may have side effects

:x++sf할 수도 있고 안 할 수도 있습니다.그래서 메시지는 다르게 쓰여져 있습니다.함수가 순수(부작용이 없음)할 수 있기 때문에 함수가 두 번 호출되는 것이 반드시 문제가 되는 것은 아닙니다.

어떻게 작동하는지 궁금하시면 여기서 받으실 수 있습니다.디버깅을 활성화하면 런타임 페널티가 발생합니다. 물론입니다.SFX_CHECK 안 함으로 할 수 것도 하지 y다:assert).

입니다.SFX_CHECK보호된 표현을 평가하고 부작용이 있는지 여부를 판단하기 위해 해석합니다.이 구문 분석은 기호 테이블 정보(식별자가 선언되는 방식)에 대한 액세스 없이 수행되기 때문에 모호합니다.파서는 여러 파싱 전략을 추구합니다.됩니다에 처리 하여 수행됩니다.setjmp/longjmp 분석 보다 를 입력한 구문 분석 결과는 향후 평가에 대한 보다 빠른 검색을 위해 텍스트 식에 키를 입력한 해시 테이블에 저장됩니다.

언급URL : https://stackoverflow.com/questions/16243509/explanation-of-c-faqs-unsafe-macro

반응형