내 연락처 정보
우편메소피아@프로톤메일.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
지난호에[C 언어] - 전처리에 대한 자세한 설명(2편) 이번 연구를 통해 전처리 과정에서 매크로에 대한 관련 지식을 자세히 소개했습니다. 모두가 많은 것을 얻을 수 있을 거라 믿습니다. 걱정하지 마세요. 이번 호에서는 전처리에 대한 다른 지식을 계속해서 배워보겠습니다.
# 运算符
매크로의 인수를 문자열 리터럴로 변환합니다.매개변수가 있는 매크로 대체 목록에 표시되는 것이 허용됩니다.# 运算符
수행되는 작업은 "문자열화"로 이해될 수 있습니다.
그게 무슨 뜻이에요?
먼저 예표를 만들어 보겠습니다.
int mian()
{
printf("hello" "worldn");
printf("helloworldn");
return 0;
}
위의 두 코드 줄의 차이점은 무엇입니까? 함께 살펴 보겠습니다.
보시다시피 두 개의 문자열과 하나의 문자열의 효과는 동일합니다.C语言会把两个字符串天然连成一个字符串
, 중간에 공백을 추가하는 것은 쓸모가 없습니다.
이제 다음과 같은 장면이 있습니다.
int main()
{
int a = 1;
printf("The value of a is %dn", a);
int b = 20;
printf("The value of b is %dn", b);
float f = 8.5f;
printf("The value of f is %fn", f);
return 0;
}
우리는 세 줄의 코드 논리가 매우 훌륭하다는 것을 발견했습니다.相像
예, 하지만 있습니다.些许不同
。
그래서 우리는 그것들이 너무 비슷하기 때문에 그것들을 넣어도 될까라고 생각했습니다.封装成一个函数
, 사용하기 쉽도록?
하지만 함수는 이 기능을 수행할 수 없습니다.
그럼 우리는 어떻게 해야 합니까?
매크로를 사용하여 해결해 볼 수 있습니다.
#define Print(n, format) printf("The value of n is " format "n", n)
int main()
{
int a = 1;
Print(a, "%d");
//printf("The value of a is %dn", a);
int b = 20;
Print(b, "%d");
//printf("The value of b is %dn", b);
float f = 8.5f;
Print(f, "%f");
//printf("The value of f is %fn", f);
return 0;
}
작업 결과:
우리는 발견한다 엔N 변경된 사항이 없는데 어떻게 수정해야 하나요?
여기서 # 연산자를 사용해야 합니다:# 매크로의 인수를 다음으로 변환합니다.문자열 리터럴,지금 바로 엔N ~이 되다 “ ” “ ”“N”
이때 스플라이싱(splicing) 방식을 사용하면 된다.
#define Print(n, format) printf("The value of " #n " is " format "n", n)
몰라?아래 설명을 읽어보시면 이해가 되실 겁니다.
##
그 사람 안에 있을 수 있어양쪽의 기호가 하나의 기호로 결합됩니다., 매크로 정의가 가능합니다.从分离的文本片段创建标识符
。##
라고마크 접착 .그러한 연결은 다음을 생성해야 합니다.合法
그렇지 않으면 결과가 정의되지 않습니다.
앞에서 두 숫자 중 더 큰 값을 찾는 함수를 작성하려면 서로 다른 데이터 유형에 대해 서로 다른 함수를 작성해야 한다고 말했습니다.
int int_max(int x, int y)
{
return x > y ? x : y;
}
float float_max(float x, float y)
{
return x > y ? x : y;
}
이것은 필연적으로 너무 번거롭고 두 기능 사이에는 많은 유사점이 있습니다.
이러한 함수를 빠르게 생성할 수 있는 방법이 있습니까?함수처럼곰팡이마찬가지입니다. 함수를 적용하면 결과가 나옵니다.
우리는 이렇게 쓸 수 있습니다宏
:
#define GENERIC_MAX(type)
type type##_max(type x, type y)
{
return x > y ? x : y;
}
#define GENERIC_MAX(type)
type type##_max(type x, type y)
{
return x > y ? x : y;
}
GENERIC_MAX(int); //相当于定义了一个函数int_max
GENERIC_MAX(float); //相当于定义了一个函数float_max
int main()
{
int r1 = int_max(3, 5);
printf("%dn", r1);
float r2 = float_max(2.3f, 7.6f);
printf("%fn", r2);
return 0;
}
작업 결과:
우리는 또한 할 수 있습니다 gcc gccg참조 전처리 결과를 환경에서 관찰.i
파일을 보다 직관적으로 이해할 수 있습니다.
물론 이렇게 생성된 함수도 디버그하기 불편하다.
그럼 여기 ##
어떤 역할을 하나요?
추가하다 ##
, 컴파일러는 그것이 기호라고 생각할 것입니다
캉가를 살펴보자##
효과:
일반적으로 함수와 매크로를 사용하는 구문은 매우 유사하므로语言本身没法帮我们区分二者
우리의 일반적인 습관 중 하나는 다음과 같습니다.
- 매크로 이름 지정모두 대문자
- 기능 이름모두 대문자를 사용하지 마세요.
물론 이러한 명명 규칙이 절대적인 것은 아닙니다.
예를 들어 오프셋 오프셋영형ff세티 이 매크로는 모두 소문자로 작성되었습니다.
메모: 오프셋 오프셋영형ff세티 구조물의 시작 위치를 기준으로 구조물 부재의 오프셋을 계산하는 데 사용됩니다.
# 정의되지 않음 정의되지 않음유N디이자형에프 지침은 다음과 같은 데 사용됩니다.매크로 정의 제거
위 코드는 169행에서 사용됩니다.# 정의되지 않음 정의되지 않음유N디이자형에프 매크로 MAX가 제거되었습니다.제거 전 168개 라인을 호출하면 문제가 없으나, 제거 후 170개 라인을 호출하면 오류가 발생합니다.
많은 C 컴파일러(VS 제외)는 명령줄에서 기호를 정의하는 기능을 제공합니다.컴파일 프로세스를 시작하는 데 사용됩니다.
예를 들어 : 이 기능은 동일한 소스 파일을 기반으로 다양한 버전의 프로그램을 컴파일하려는 경우에 유용합니다. (프로그램에서 특정 길이의 배열이 선언되었다고 가정해 보겠습니다. 머신 메모리가 제한되어 있으면 아주 작은 배열이 필요하지만, 다른 머신의 메모리가 크다면 필요한 배열은 더 커질 수 있습니다.)
명령줄 정의는 다음과 같습니다.전처리 단계위 코드에서 전처리 단계에서 처리됨 사이즈 사이즈사이즈 의 가치가 결정되었습니다.
프로그램을 컴파일할 때 하나(명령문 그룹)를 컴파일하거나 폐기하는 것은 매우 어렵습니다.方便
의.우리가 사용할 수 있기 때문에조건부 컴파일 지시어
조건부 컴파일 명령은 다음 코드입니다.我想让你编译就编译,不想让你编译你就不要编译了
. 조건을 설정할 수 있습니다. 조건이 true이면 이 코드는 컴파일됩니다. 조건이 false이면 이 코드는 컴파일되지 않습니다.
예를 들어:
일부 디버깅 코드를 삭제하는 것은 아쉽지만 그대로 유지하는 데 방해가 되기 때문에 선택적으로 컴파일할 수 있습니다.
일반적으로 사용되는 조건부 컴파일 지침:
#if 常量表达式
//···
#endif
#if 常量表达式
//···
#elif 常量表达式
//···
#else
//···
#endif
어떤 진술이 참인지, 그 진술이 실행되는지
#define M 1
int main()
{
#if M == 0
printf("hellon");
#elif M == 1
printf("worldn");
#elif M == 2
printf("csdnn");
#endif
printf("886n");
return 0;
}
#if defined(symbol)
#ifdef symbol
//上面两个的反面
if !defined(symbol)
#ifndef symbol
#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif
# include "filename"
검색 전략: 첫 번째소스 파일이 위치한 프로젝트 디렉터리헤더 파일을 찾을 수 없으면 컴파일러는 동일한 방식으로 라이브러리 함수 헤더 파일을 검색합니다.표준 위치 조회헤드 파일
다시 찾을 수 없다면컴파일 오류
리눅스 리눅스엘~에유엑스 환경의 표준 헤더 파일 경로(헤더 파일이 배치되는 위치):
/usr/include
VS 환경의 표준 헤더 파일 경로:
C:Program Files (x86)Microsoft Visual Studio 12.0VCinclude
//这是VS2013的默认路径
#include <filename.h>
헤더 파일을 찾아 다음으로 직접 이동하세요.표준 경로가서 검색해 보세요. 찾을 수 없으면 메시지가 표시됩니다.컴파일 오류。
이는 라이브러리 파일에도 사용할 수 있다는 의미입니까?“ ”
양식에는 다음이 포함됩니다.
정답은확증하다예, 하지만 검색은 이렇게 이루어집니다.덜 효율적, 물론 이것도 마찬가지다구별하기 쉽지 않음라이브러리 파일인가요 아니면 로컬 파일인가요?
이전(컴파일 및 링크)을 연구한 결과 전처리 단계에 헤더 파일이 포함되어 있음을 알 수 있습니다.直接将该文件的代码拷贝到包含头文件的地方
헤더 파일이 10번 포함되면 실제로는 10번 컴파일됩니다. 반복적으로 포함되면 컴파일에 대한 부담이 커집니다.
테스트 .c 테스트.c티에스티.씨
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
int main()
{
return 0;
}
테스트 .h 테스트.h티에스티.시간
void test();
struct Stu
{
int id;
char name[20];
};
하지만 프로젝트에서는 필연적으로 파일이 여러 번 포함되는데, 이 문제를 어떻게 해결해야 할까요?
답변:조건부 컴파일
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif
그것을 이해하는 방법?
- 헤더 파일을 처음 포함할 때 컴파일해야 합니까?먼저 판단하다
- __TEST_H__ 기호는 다음과 같습니다.정의되지 않음,원하다엮다
- 직후__TEST_H__ 기호 정의
- 그런 다음 헤더 파일을 다시 포함하고 __TEST_H__를 찾으십시오.정의되었습니다,더이상나중에 포함되는 헤더 파일에 대해서는 다음을 수행하십시오.엮다
그러나 위의 작성 방법이 더 번거롭습니다.
#pragma once
효과는 위의 방법과 같습니다
이렇게 하면 헤더 파일이 반복적으로 도입되는 것을 방지할 수 있습니다.
#error
#pragma
#line
···
#pragma pack()//在结构体部分介绍
관심있는 친구들이 읽을 수 있습니다 "C 언어에 대한 심층 분석"
좋습니다. 이번 호의 전처리에 대한 지식은 여기까지입니다. 이 블로그가 여러분에게 도움이 되기를 바랍니다. 동시에, 틀린 부분이 있으면 바로잡아주시고, C언어 학습의 길에서 함께 발전해 나가도록 해주세요!