programing

C 프리프로세서는 순환 의존관계를 어떻게 처리합니까?

procenter 2022. 8. 17. 23:35
반응형

C 프리프로세서는 순환 의존관계를 어떻게 처리합니까?

C 프리프로세서가 (#defines의) 순환 의존관계를 어떻게 처리하는지 알고 싶습니다.이것은 제 프로그램입니다.

#define ONE TWO 
#define TWO THREE
#define THREE ONE

int main()
{
    int ONE, TWO, THREE;
    ONE = 1;
    TWO = 2;
    THREE = 3;
    printf ("ONE, TWO, THREE = %d,  %d, %d \n",ONE,  TWO, THREE);
}

프리프로세서의 출력을 다음에 나타냅니다.왜 출력이 그렇게 되는지 알 수 없습니다.이 경우 프리프로세서가 수행하는 다양한 절차를 알고 싶습니다.

# 1 "check_macro.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "check_macro.c"

int main()
{
 int ONE, TWO, THREE;
 ONE = 1;
 TWO = 2;
 THREE = 3;
 printf ("ONE, TWO, THREE = %d,  %d, %d \n",ONE, TWO, THREE);
}

Linux 3.2.0-49-generic-pae에서 이 프로그램을 실행하여 gcc 버전 4.6.3(Ubuntu/Linaro 4.6.3-1ubuntu5)으로 컴파일하고 있습니다.

https://gcc.gnu.org/onlinedocs/cpp/Self-Referential-Macros.html#Self-Referential-Macros는 자기 참조 매크로에 관한 질문에 답합니다.

답변의 핵심은 프리프로세서가 자기 참조 매크로를 발견해도 매크로를 전혀 확장하지 않는다는 것입니다.

순환적으로 정의된 매크로의 확장을 막기 위해서도 같은 논리가 사용되고 있다고 생각합니다.그렇지 않으면 프리프로세서는 무한 확장 상태가 됩니다.

프리프로세서 매크로가 전개되고 있는 동안, 그 매크로의 이름은 전개되지 않습니다.따라서 세 가지 기호는 모두 그 자체로 정의됩니다.

ONE -> TWO -> THREE -> ONE (not expanded because expansion of ONE is in progress)
TWO -> THREE -> ONE -> TWO (        "                         TWO      "        )
THREE -> ONE -> TWO -> THREE (      "                         THREE    "        )

이러한 행동은 C 표준의 § 6.10.3.4에 의해 설정된다(C11 초안의 섹션 번호는 내가 아는 한 섹션의 문구와 번호는 C89 이후 변경되지 않았다).그 정의(및 「마크로명」)로 됩니다.# ★★★★★★★★★★★★★★★★★」##preprocessor prep prep prep prep prep prep prep prep prep prep prep 。으로 (파일의 더. ( ) 、 ( ) 。

2/ 이 교환 리스트의 스캔중에 교환되고 있는 매크로의 이름이 발견되었을 경우(소스 파일의 전처리 토큰은 제외), 교환되지 않습니다.게다가 네스트 된 치환에 의해서, 치환되고 있는 매크로의 이름이 발견되었을 경우는, 치환되지 않습니다.

이 절에서는 재귀 호출로 인해 대체되지 않은 토큰은 사실상 "동결"되어 있으며, 대체되지 않습니다.

…이러한 대체되지 않은 매크로명의 전처리 토큰은, 그 매크로명의 전처리 토큰이 교환되었을 가능성이 있는 컨텍스트에서 나중에(재검사) 되더라도, 더 이상 교환할 수 없습니다.

마지막 문장이 언급하는 상황은 실제로는 거의 발생하지 않지만, 내가 생각할 수 있는 가장 간단한 예를 다음에 제시하겠습니다.

#define two one,two
#define a(x) b(x)
#define b(x,y) x,y
a(two)

그 결과는one, two.two로 확장됩니다.one,two치환 중에a, 및 확장two완전히 전개된 것으로 표시됩니다.그 후,b(one,two)확장됩니다.이것은 더 이상 의 치환의 맥락이 아닙니다.two단,two두 번째 논거입니다.b동결되었으므로 다시 확장되지 않습니다.

이 질문에 대한 답변은 ISO/IEC 9899에서 확인할 수 있습니다.TC2 섹션 6.10.3.4 "재개척 및 추가 교체", 단락 2. 귀하의 편의를 위해 여기에 인용합니다. 향후 사양에 대한 질문이 있을사양서를 읽어보시기 바랍니다.

이 대체 목록 검사 중에 대체되는 매크로의 이름이 발견되면(원본 파일의 나머지 전처리 토큰은 제외) 대체되지 않습니다.게다가 네스트 된 치환에 의해서 치환되는 매크로의 이름이 발견되었을 경우, 치환되지 않습니다.이러한 대체되지 않은 매크로 이름 전처리 토큰은 나중에 해당 매크로 이름 전처리 토큰이 대체되었을 수 있는 컨텍스트에서 다시 검사(재검사)되더라도 더 이상 대체할 수 없습니다.

이 예에서는 같은 이름의 변수를 정의하기 전에 매크로 처리를 하기 때문에 매크로 처리의 결과에 관계없이 항상 인쇄합니다.1, 2, 3!

다음으로 변수를 먼저 정의하는 예를 나타냅니다.

#include <stdio.h>
int main()
{
    int A = 1, B = 2, C = 3;
#define A B
#define B C
//#define C A
    printf("%d\n", A);
    printf("%d\n", B);
    printf("%d\n", C);
}

이 인쇄물은3 3 3은근히 코멘트가 없다.#define C A회선의 동작을 변경하다printf("%d\n", B);

다음은 Rici와 Eric Lippert의 답변에 설명된 동작의 좋은 예입니다. 즉, 이미 동일한 매크로를 확장 중 다시 매크로 이름이 발견되면 다시 확장되지 않습니다.

내용test.c:

#define ONE 1, TWO
#define TWO 2, THREE
#define THREE 3, ONE

int foo[] = {
  ONE,
  TWO,
  THREE
};

출력gcc -E test.c(초기 제외)# 1 ...회선):

int foo[] = {
  1, 2, 3, ONE,
  2, 3, 1, TWO,
  3, 1, 2, THREE
};

(댓글로서 투고하고 싶지만, 코멘트에 상당한 코드 블록을 포함시키는 것은 좀 어색하기 때문에 커뮤니티 위키로 대신하겠습니다.기존 답변에 포함시키는 것이 좋다고 생각되는 경우 복사하여 이 CW 버전을 삭제하도록 요청하십시오.)

언급URL : https://stackoverflow.com/questions/24177503/how-does-the-c-preprocessor-handle-circular-dependencies

반응형