📜  C / C++预处理器

📅  最后修改于: 2021-05-26 02:40:56             🧑  作者: Mango

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

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

预处理程序提供预处理程序指令,这些指令告诉编译器在编译之前对源代码进行预处理。所有这些预处理程序指令均以“#”(哈希)符号开头。 “#”符号表示无论以#开头的任何语句都将进入预处理程序,并且预处理程序将执行此语句。一些预处理器指令的实例为:的#include#define的#ifndef等记住,#符号仅提供其将去到预处理器的路径,并且命令例如包括由预处理器程序处理。例如,include将在程序中包含额外的代码。我们可以将这些预处理程序指令放置在程序中的任何位置。

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

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

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

  • :宏是程序中的一段代码,具有一定的名称。只要编译器遇到此名称,编译器就会用实际的代码段替换该名称。 “ #define”指令用于定义宏。现在让我们借助程序来了解宏定义:
C++
#include 
 
// macro definition
#define LIMIT 5
int main()
{
    for (int i = 0; i < LIMIT; i++) {
        std::cout << i << "\n";
    }
 
    return 0;
}


C
#include 
 
// macro definition
#define LIMIT 5
int main()
{
    for (int i = 0; i < LIMIT; i++) {
        printf("%d \n",i);
    }
 
    return 0;
}


C++
#include 
 
// 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;
}


C
#include 
 
// macro with parameter
#define AREA(l, b) (l * b)
int main()
{
    int l1 = 10, l2 = 5, area;
 
    area = AREA(l1, l2);
 
    printf("Area of rectangle is: %d", area);
 
    return 0;
}


C++
#include 
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;
}
 
// This code is contributed by shivanisinghss2110


C
#include 
 
void func1();
void func2();
 
#pragma startup func1
#pragma exit func2
 
void func1()
{
    printf("Inside func1()\n");
}
 
void func2()
{
    printf("Inside func2()\n");
}
 
int main()
{
    void func1();
    void func2();
    printf("Inside main()\n");
 
    return 0;
}


C
#include 
 
void func1();
void func2();
 
void __attribute__((constructor)) func1();
void __attribute__((destructor)) func2();
 
void func1()
{
    printf("Inside func1()\n");
}
 
void func2()
{
    printf("Inside func2()\n");
}
 
int main()
{
    printf("Inside main()\n");
 
    return 0;
}


输出:

0
1
2
3
4

在上面的程序中,编译器执行LIMIT时,将其替换为5。宏定义中的LIMIT被称为宏模板,而5则是宏扩展。

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

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

C++

#include 
 
// 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;
}

C

#include 
 
// macro with parameter
#define AREA(l, b) (l * b)
int main()
{
    int l1 = 10, l2 = 5, area;
 
    area = AREA(l1, l2);
 
    printf("Area of rectangle is: %d", 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”语句将评估为false。

  • #pragma指令:此指令是一种特殊用途的指令,用于打开或关闭某些功能。这种类型的指令是特定于编译器的,即它们随编译器的不同而不同。下面讨论了一些#pragma指令:
    • #pragma startup#pragma exit :这些指令可帮助我们指定程序启动之前(控件传递到main()之前)和程序退出之前(控件从main()返回之前)所需运行的功能。 。
      注意:以下程序不适用于GCC编译器。
      看下面的程序:

C++

#include 
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;
}
 
// This code is contributed by shivanisinghss2110

C

#include 
 
void func1();
void func2();
 
#pragma startup func1
#pragma exit func2
 
void func1()
{
    printf("Inside func1()\n");
}
 
void func2()
{
    printf("Inside func2()\n");
}
 
int main()
{
    void func1();
    void func2();
    printf("Inside main()\n");
 
    return 0;
}

输出:

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

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

Inside main()

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

C

#include 
 
void func1();
void func2();
 
void __attribute__((constructor)) func1();
void __attribute__((destructor)) func2();
 
void func1()
{
    printf("Inside func1()\n");
}
 
void func2()
{
    printf("Inside func2()\n");
}
 
int main()
{
    printf("Inside main()\n");
 
    return 0;
}
  • #pragma warn指令:该指令用于隐藏在编译过程中显示的警告消息。
    我们可以隐藏警告,如下所示:
    • 警告的#pragma -rvl:这个指令皮革时应该返回一个值的函数没有返回值被上调的警告。
    • #pragma warn -par :此伪指令隐藏那些在函数不使用传递给它的参数时引发的警告。
    • #pragma warn -rch :此伪指令将隐藏无法访问代码时发出的警告。例如:在函数的return语句之后编写的任何代码均不可访问。
想要从精选的最佳视频中学习和练习问题,请查看《基础知识到高级C的C基础课程》。