1. 主页
  2. 文档
  3. C++教程
  4. C++基础教程
  5. C++ 预处理器

C++ 预处理器

顾名思义,预处理器是在编译之前处理我们的源代码的程序。在 C / C++ 中编写程序和执行程序之间涉及许多步骤。在我们真正开始学习预处理器之前,让我们先看看这些步骤。

您可以在上图中看到中间步骤。程序员编写的源代码存储在文件 program.c 中。该文件然后由预处理器处理,并生成一个扩展的源代码文件,命名为 program. 此扩展文件由编译器编译,并生成名为 program .obj 的目标代码文件。最后,链接器将这个目标代码文件链接到库函数的目标代码,生成可执行文件program.exe。 

预处理器程序提供预处理器指令,告诉编译器在编译之前预处理源代码。所有这些预处理器指令都以“#”(哈希)符号开头。’#’ 符号表示,任何以# 开头的语句都将进入预处理程序,预处理程序将执行该语句。一些预处理器指令的例子有:#include、 # define、 # ifndef等。请记住,# 符号只提供了一条通往预处理器的路径,而诸如 include 之类的命令由预处理器程序处理。例如,include 将在您的程序中包含额外的代码。我们可以将这些预处理器指令放置在程序中的任何位置。 

预处理器指令有 4 种主要类型:

  1. 文件包含
  2. 条件编译
  3. 其他指令

现在让我们详细了解这些指令中的每一个。 

  • :宏是程序中的一段代码,它被赋予了一些名称。每当编译器遇到此名称时,编译器都会将该名称替换为实际的代码段。’#define’ 指令用于定义宏。现在让我们借助程序来理解宏定义:
#include <iostream>

// macro definition
#define LIMIT 5
int main()
{
for (int i = 0; i < LIMIT; i++) {
std::cout << i << "\n";
}

return 0;
}

输出:

0
1
2
3
4

在上述程序中,当编译器执行单词 LIMIT 时,它会将其替换为 5。宏定义中的单词“LIMIT”称为宏模板,“5”是宏扩展。 

注意:宏定义的末尾没有分号(’;’)。宏定义不需要分号结束。

带参数的宏:我们也可以将参数传递给宏。使用参数定义的宏与函数类似。让我们通过一个程序来理解这一点: 

#include <iostream>

// macro with parameter
#define AREA(l, b) (l * b)
int main()
{
int l1 = 10, l2 = 5, area;

area = AREA(l1, l2);

std::cout << "Area of rectangle is: " << area;

return 0;
}

输出:

Area of rectangle is: 50

我们可以从上面的程序中看到,每当编译器在程序中找到 AREA(l, b) 时,它就会用语句 (l*b) 替换它。不仅如此,传递给宏模板 AREA(l, b) 的值也将在语句 (l*b) 中被替换。因此 AREA(10, 5) 将等于 10*5。 

  • 文件包含:这种类型的预处理器指令告诉编译器在源代码程序中包含一个文件。用户可以在程序中包含两种类型的文件: 
    • 头文件或标准文件:这些文件包含预定义函数的定义,如 printf()、scanf() 等。必须包含这些文件才能使用这些函数。不同的函数在不同的头文件中声明。例如,标准 I/O 函数位于“iostream”文件中,而执行字符串操作的函数位于“字符串”文件中。 
      语法
#include< file_name >

其中file_name是要包含的文件的名称。'<‘ 和 ‘>’ 括号告诉编译器在标准目录中查找文件。 

  • 用户定义文件:当程序变得非常大时,最好将其分成较小的文件并在需要时包含。这些类型的文件是用户定义的文件。这些文件可以包含为:
#include"filename"
  • 条件编译:条件编译指令是一种指令类型,有助于编译程序的特定部分或根据某些条件跳过程序的某些特定部分的编译。这可以借助两个预处理命令“ ifdef ”和“ endif ”来完成。 
    语法
#ifdef macro_name
statement1;
statement2;
statement3;
.
.
.
statementN;
#endif

如果定义了名为“ macroname ”的宏,则语句块将正常执行,但如果未定义,编译器将简单地跳过该语句块。 

  • 其他指令:除了上述指令外,还有两个不常用的指令。这些是: 
    • #undef 指令:#undef 指令用于取消定义现有宏。该指令的作用是:
#undef LIMIT

使用此语句将取消定义现有的宏 LIMIT。在此语句之后,每个“#ifdef LIMIT”语句都将评估为假。 

  • #pragma 指令:该指令是一个特殊用途的指令,用于打开或关闭某些功能。这种类型的指令是特定于编译器的,即它们因编译器而异。下面讨论了一些#pragma 指令: 
    • #pragma startup#pragma exit:这些指令帮助我们指定在程序启动之前(控制传递给 main() 之前)和程序退出之前(就在控制从 main() 返回之前)需要运行的函数. 
      注意:以下程序不适用于 GCC 编译器。 
      看下面的程序:
#include <bits/stdc++.h>
using namespace std;

void func1();
void func2();

#pragma startup func1
#pragma exit func2

void func1()
{
cout << "Inside func1()\n";
}

void func2()
{
cout << "Inside func2()\n";
}

int main()
{
void func1();
void func2();
cout << "Inside main()\n";

return 0;
}

输出: 

Inside func1()
Inside main()
Inside func2()

上面的代码在 GCC 编译器上运行时将产生如下输出: 

Inside main()

发生这种情况是因为 GCC 不支持 #pragma 启动或退出。但是,您可以将以下代码用于 GCC 编译器上的类似输出。

#include <iostream>
using namespace std;

void func1();
void func2();

void __attribute__((constructor)) func1();
void __attribute__((destructor)) func2();

void func1()
{
printf("Inside func1()\n");
}

void func2()
{
printf("Inside func2()\n");
}

// Driver code
int main()
{
printf("Inside main()\n");

return 0;
}
  • #pragma warn 指令:该指令用于隐藏编译期间显示的警告消息。 
    我们可以隐藏警告,如下所示: 

    • #pragma warn -rvl:该指令隐藏那些在应该返回值的函数没有返回值时引发的警告。
    • #pragma warn -par:该指令隐藏那些在函数不使用传递给它的参数时引发的警告。
    • #pragma warn -rch:此指令隐藏在代码无法访问时引发的警告。例如:函数中的return语句之后编写的任何代码都是无法访问的。
这篇文章对您有用吗?