기술나눔

[C 언어] - 전처리에 대한 자세한 설명(1편)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. 사전 정의된 기호

C 언어에는 몇 가지 설정이 있습니다.사전 정의된 기호, 직접 사용할 수 있으며 사전 정의된 기호는 다음과 같습니다.在预处理期间处理的

__FILE__    //进行编译的源文件
__LINE__	//文件当前的行号
__DATE__	//文件被编译日期
__TIME__	//文件被编译那一瞬的时间
__STDC__	//如果编译器遵循ANSI C(标准C),其值为1,否则未定义(报错)
  • 1
  • 2
  • 3
  • 4
  • 5

  
예:

#include<stdio.h>

int main()
{
	printf("%sn", __FILE__);
	printf("%dn", __LINE__);
	printf("%sn", __DATE__);
	printf("%sn", __TIME__);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  
작업 결과:

여기에 이미지 설명을 삽입하세요.

VS는 ANSI C(표준 C)를 완벽하게 지원하지 않는다는 점을 여기서 언급하고 싶습니다. gcc gccg참조 ANSI C(표준 C) 지원
  

둘,# 정의하다 정의하다이자형에프~에이자형 상수(기호) 정의

기본 구문:

# define name stuff
  • 1

예를 들어:

#define MAX 100
#define reg register  // 为 register 这个关键字创建一个简短的名字
#define do_forever for(;;)  //用更形象的符号来替换一种实现
#define CASE break;case		//在写 case 语句的时候自动吧 break 写上

//如果定义的 stuff 过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)
#define DEBUG_PRINT printf("file:%stline:%dt 
							date:%sttime:%sn" ,
							__FILE__,__LINE__ , 
							__DATE__,__TIME__ )
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 두 번째 문장은 게으름인데 왜요?
  • 세 번째 문장 에 대한 에 대한에프또는 회보初始化、判断、调整都可以省略, 그러나 판단이 생략되면 판단 조건이 항상 참이라는 뜻, 즉무한 루프
  • 네 번째 게임에서는 이 작업을 수행하지 않는 것이 가장 좋습니다. 쉽게 문제가 발생할 수 있습니다.
  • 다섯 번째 문장줄 연속 문자분기 후 문제를 방지하는 것이 그 본질입니다.转义뒤쪽에回车符 , 캐리지 리턴이 더 이상 캐리지 리턴이 아닙니다.줄 연속 문자 뒤에는 아무 것도 올 수 없습니다.“” Enter를 누르기만 하면 됩니다. 그렇지 않으면 다음 코드 줄이 계속되지 않습니다.

  
이제 문제가 발생합니다. #을 사용하세요. 정의하다 정의하다이자형에프~에이자형 식별자를 정의할 때 끝에 추가해야 합니까?

예를 들어:

#define MAX 1000
#define MAX 1000;

int main()
{
	int n = MAX;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

여기에 이미지 설명을 삽입하세요.

위의 코드 플러스, 약간 중복되는 것처럼 보이지만 프로그램 실행에는 영향을 미치지 않습니다.
추가하는 것 같더라구요 아니면 추가하지 마세요 괜찮을까요?
정말 이런거야?
  
다음 예를 살펴보겠습니다.

//例一
int main()
{
	printf("%dn", MAX);
	return 0;
}

//例二
int mian()
{
	int max = 0;
	if (1)
		max = MAX;
	else
		max = 1;
	return 0
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

교체 후:

printf("%dn", 1000;);
  • 1

인쇄1000;무슨 뜻인가요?

if (1)
	max = 1000;
	;
else
	max = 1;
  • 1
  • 2
  • 3
  • 4
  • 5

그 밖에 그 밖에이자형 누구랑 어울릴까?
  
알다시피, 이건 뭔가 문제가 있어요.사용 # 정의하다 정의하다이자형에프~에이자형 지표를 정의할 때 뒤에 추가하지 마십시오.

  

삼,# 정의하다 정의하다이자형에프~에이자형 매크로 정의

  # 정의하다 정의하다이자형에프~에이자형 메커니즘에는 다음 조항이 포함됩니다.允许把参数替换到文本中, 이 구현은 종종 호출됩니다.매크로 (마르코) (마르코)엄마알코또는 매크로 정의 ( definemacro ) ( define macro)이자형에프~에이자형엄마크로
위의 매크로와 매크로 정의 식별자의 차이점은 다음과 같습니다.매크로에는 매개변수가 있습니다.
  
매크로가 선언되는 방법은 다음과 같습니다.

#define name(parament - list) stuff
  • 1

그들 중 하나 매개변수 매개변수아르 자형~이다이자형N - 목록 목록에스(매개변수 목록)은 다음에 나타날 수 있는 쉼표로 구분된 기호 목록입니다. 물건 물건에스ff 가운데
메모: 매개변수 매개변수아르 자형~이다이자형N - 목록 목록에스(파라미터 목록)왼쪽 괄호반드시 함께 있어야 합니다 이름 이름이름이자형 옆에, 사이에 공백이 있으면 인수 목록은 다음과 같이 해석됩니다. 물건 물건에스ff 의 일부.
  

예:

//实现一个宏,计算一个数的平方
#define SQUARE(x) x*x

int main()
{
	int a = 5;
	int ret = SQUARE(a);
	printf("%dn", ret);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  
작업 결과:

여기에 이미지 설명을 삽입하세요.

보시다시피 5의 제곱이 올바르게 계산되었습니다.

그러나 실제로 위 코드에는 문제가 있습니다. 다음 코드 조각을 참조하세요.

int a = 5;
printf("%dn", SQUARE(a + 1));
  • 1
  • 2

  
작업 결과:

여기에 이미지 설명을 삽입하세요.

왜 그럴까요? 5+1의 결과는 36, 6 ∗ * 6은 36이 되어야 하는데 어떻게 11을 얻었나요?

문제는 매크로에 있습니다.宏是直接替换, 위 코드를 직접 대체한 결과는 다음과 같습니다.

printf("%dn", a+1*a+1);
  • 1

5 + 6 + 1, 결과는 당연히 11

매크로 정의의 양쪽에 괄호를 추가하면 이 문제를 쉽게 해결할 수 있습니다.

#define SQUARE(x) (x)*(x)
  • 1

그렇다면 이 정의에는 문제가 없는 걸까요?다음 매크로 정의를 살펴보겠습니다.

#define DOUBLE(X) (X)+(X)
  • 1

이전 문제를 피하기 위해 정의에 괄호를 사용했지만 이 매크로로 인해 새로운 오류가 발생할 수 있습니다.

int a = 5;
printf("%dn", 10 * DOUBLE(a));
  • 1
  • 2

  
작업 결과:

여기에 이미지 설명을 삽입하세요.

출력 결과는 100이 아니고 55입니다. 이유는 위와 같습니다. 우선순위 질문
  
해결책:

#define DOUBLE(X) ((X)+(X))
  • 1

요약하자면, 매크로를 사용할 때절대로 괄호를 무시하지 마세요매크로를 사용할 때 매개변수의 연산자나 인접 연산자 간의 예상치 못한 상호 작용을 방지합니다.

  

4. 부작용이 있는 매크로 매개변수

매크로 정의에 매크로 매개변수가 두 번 이상 나타나는 경우 매개변수에부작용, 그러면 이 매크로를 사용할 때 위험에 처할 수 있습니다.不可预测 의 결과. 부작용은 표현식을 평가할 때 영구적인 효과입니다.
  
예를 들어:

x + 1; //不带副作用
x++//带副作用
  • 1
  • 2

다음 MAX 매크로는 부작용이 있는 매개변수로 인해 발생하는 문제를 보여줍니다.

#define MAX(a,b) ((a) > (b) ? (a):(b))

int main()
{
	int x = 5;
	int y = 8;
	int z = MAX(x++, y++);
	printf("x=%d, y=%d, z=%dn", x, y, z);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  
작업 결과:

여기에 이미지 설명을 삽입하세요.

왜 그럴까요?같이 분석해보자

z = ((X++) > (y++) ? (x++) : (y++))
  • 1
  • 먼저 판단하십시오. 더블 엑스엑스++ 와 와이++ 판단, 접미사이므로 ++ 판단할 때 더블 엑스엑스 5,와이 8, 8 &gt; 5입니다.
  • 심사 후 더블 엑스엑스 6,와이 9인용
  • 그런 다음 실행 와이 ++, 후위가 붙어 있으므로 ++ 결과는 9입니다.
  • 그럼 계속하세요 와이 자동 증가를 수행하고,와이 최종 결과는 10

우리는 할 것이다 더블 엑스엑스 그리고와이 매크로에 전달되면 결과가 변경되었습니다. 특히와이, 두 번 변경한 후 끔찍하지 않다고 말씀하셨습니다.
부작용이 있는 매개변수가 매크로에 전달되고 매개변수가 매크로에 두 번 이상 나타나는 경우 해당 매개변수의 부작용도 두 번 이상 나타납니다.
  

5. 매크로 교체 규칙

프로그램 #에서 확장 정의하다 정의하다이자형에프~에이자형 심볼과 매크로를 정의할 때 관련된 여러 단계가 있습니다.

  • 매크로를 호출할 때 먼저参数进行检查, #이 포함되어 있는지 확인하세요. 정의하다 정의하다이자형에프~에이자형 한정된标识符 .그렇다면 먼저 교체됩니다.
  • 대체 텍스트는 다음과 같습니다.被插入매크로 매개변수 이름을 해당 값으로 대체하여 프로그램의 원래 위치로 이동합니다.
#define MAX(a,b) ((a) > (b) ? (a):(b))
#define M 10

int main()
{
	int x = 5;
	int z = MAX(x, M);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 최대(x, M) 최대(x, M)엑스(엑스,) 가운데 MM 먼저 10으로 대체되고 10이 원본에 삽입됩니다. MM 위치
  • 마지막으로 결과를 다시 스캔하여 #으로 인해 발생한 내용이 포함되어 있는지 확인합니다. 정의하다 정의하다이자형에프~에이자형 정의된 기호가 있는 경우 위 프로세스를 반복합니다.

위 코드의 MAX는 #으로도 표현됩니다. 정의하다 정의하다이자형에프~에이자형 한정된매크로 .지난번 검사에서 해당 매개변수 M이 교체되었습니다. 이번에는 이를 교체해야 합니다.
   최대(x, 10) 최대(x, 10)엑스(엑스,10) 로 대체됩니다 ( ( x ) &gt; ( 10 ) ((x) &gt; (10)((엑스)>(10) ? ? ? ( x ) : ( 10 ) ) (x):(10))(엑스):(10))
  
틀림없이,宏里面嵌套宏그것도 괜찮아

MAX(x, MAX(2, 3))
  • 1

이때 먼저 매개변수에 있는 매크로를 교체한 후 전체 매크로를 교체합니다.
그러나 주목해야 할 점은 이것이다.不是递归, 이는 다른 매크로에 대한 인수 역할을 하는 매크로일 뿐입니다.递归是宏内部又调用了宏本身
또한 전처리기가 #을 검색할 때 정의하다 정의하다이자형에프~에이자형 기호를 정의할 때,문자열 상수의 내용은 검색되지 않습니다.
그게 무슨 뜻이에요?밤을 주면 이해하게 될 것입니다.

#define M 10

int main()
{
	int x = 5;
	int z = MAX(x, MAX(2, 3));
	printf("M = %d", x);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

위의 코드에서printf("M = %d", x);가운데 MM 10으로 대체되지 않습니다.

  

6. 매크로와 함수의 비교

위의 매크로는 두 숫자 중 더 큰 값을 찾는 데 사용됩니다. 이를 완전히 함수로 작성할 수 있습니다.

int Max(int x, int y)
{
	return x > y ? x : y;
}
  • 1
  • 2
  • 3
  • 4

우리는 그것들이 모두 동일한 기능을 수행한다는 것을 발견했습니다.그러나 "두 숫자 중 더 큰 숫자 찾기" 기능의 경우 매크로를 작성하면更有优势일부
  
두 가지 이유가 있습니다:

  • 사용调用函数그리고函数返回~의암호이 작은 계산을 실제로 수행하는 것보다 시간이 더 걸릴 수 있습니다(함수를 호출할 때).스택 프레임 생성 ).따라서 매크로 비율 기능은 프로그램에 있습니다.规模그리고速度그 어느 때보다 나아졌습니다
  • 더 중요한 것은函数的参数必须声明类型 , 이는 해당 함수가 적절한 유형의 표현식에만 사용되도록 합니다.반대로 이 매크로는 정수, 긴 정수, 부동 소수점 등 다양한 유형에 적용할 수 있습니다.
    > 비교하기 위해서.매크로의 매개변수는 다음과 같습니다.유형 독립적~의

앞으로도 항상 매크로를 사용하게 될까요?실제로 매크로는 다음 용도로만 사용됩니다.간단한 계산, 매크로에 비해 복잡하고 대규모 작업 및 기능에는 적합하지 않습니다. 단점

  1. 매크로가 사용될 때마다 매크로 정의 코드의 복사본이 프로그램에 삽입됩니다.매크로가 상대적으로 짧지 않은 한,大幅度增加程序的长度
  2. 매크로는没法调试~의
  3. 매크로는 유형 독립적입니다. 즉,不够严谨
  4. 매크로는 작업 우선 순위 문제를 발생시켜 프로그램이容易出错

하지만 때로는 함수가 할 수 없는 일을 매크로가 할 수도 있습니다.
  
예를 들어:

int* p = (int*)malloc(10 * sizeof * (int));
  • 1

우리는 이런 식으로 쓰는 것을 좋아하지 않습니다 멀록 멀록엄마나는오씨 기능이 너무 번거로워서 하고 싶어요大小그리고类型넘겨서 공간을 열어주세요

Malloc(10, int);
  • 1

함수가 이것을 할 수 있나요?아니, 왜냐면함수는 유형을 전달할 수 없습니다.
그리고 매크로는 그것을 할 수 있습니다. 왜냐하면 매크로는 그렇지 않기 때문입니다.확인하지 마세요당신의 매개변수유형~의

#define Malloc(n, type) (type*)malloc(n * sizeof(type))

int main()
{
	int* p = Malloc(10, int);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  
매크로와 함수의 비교:

속성# 정의하다 정의하다이자형에프~에이자형매크로 정의기능
코드 길이사용할 때마다 매크로 코드가 프로그램에 삽입됩니다. 매우 작은 매크로를 제외하면 프로그램 길이가 크게 늘어납니다.함수 코드 값은 한곳에 나타나며, 함수를 사용할 때마다 그 곳의 동일한 코드가 호출됩니다.
실행 속도더 빠르게함수 호출 및 반환(스택 프레임 열기)에 추가 오버헤드가 있으므로 느린 것부터 시작하겠습니다.
연산자 우선순위매크로 매개변수는 주변의 모든 표현식의 맥락에서 평가됩니다. 괄호가 포함되지 않으면 인접한 연산자의 우선순위로 인해 예측할 수 없는 결과가 발생할 수 있습니다. 따라서 매크로를 더 많은 괄호와 함께 작성하는 것이 좋습니다.함수 매개변수는 함수가 호출될 때 한 번만 평가되고 그 결과 값이 함수에 전달되므로 표현식의 평가 결과를 더 예측하기 쉽습니다.
부작용이 있는 매개변수매개변수는 매크로의 여러 위치로 대체될 수 있습니다. 매크로의 매개변수가 여러 번 평가되면 부작용이 있는 매개변수 평가로 인해 예측할 수 없는 결과가 발생할 수 있습니다.함수 매개변수는 매개변수를 전달할 때 한 번만 호출되므로 결과를 예측하기가 더 쉽습니다.
매개변수 유형매크로의 매개변수는 유형과 관련이 없으며 매개변수에 대한 조작이 합법적인 한 모든 매개변수 유형과 함께 사용할 수 있습니다.함수의 매개변수는 유형과 관련되어 있습니다. 매개변수의 유형이 다르면 동일한 작업을 수행하더라도 다른 함수가 필요합니다.
디버그매크로는 디버깅이 불편합니다기능을 단계별로 디버깅할 수 있습니다.
재귀매크로는 재귀적일 수 없습니다.함수는 재귀적일 수 있습니다.

장점을 결합할 수 있는 방법이 있나요?
C++에서 도입됨인라인 함수 인라인 인라인~에~에이자형 ——매크로와 함수의 장점을 모두 가지고 있습니다.
매크로만큼 빠르게 실행되지만 함수와 동일한 효과를 갖습니다.