[C语言教程]十一、预处理

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(www.zengqiang.org
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/11418890.html 
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

一、宏定义

  预处理命令可以改变程序设计环境,提高编程效率,它们并不是 C 语言本身的组成部分,不能直接对 它们进行编译,必须在对程序进行编译之前,先对程序中这些特殊的命令进行“预处理” 。经过预处理后,程序就不再包括预处理命令了,最后再由编译程序对预处理之后的源程序进行编译处理,得到可供执行的 目标代码。C 语言提供的预处理功能有三种,分别为宏定义、文件包含和条件编译,下面将对它们进行简 单介绍。 
宏定义 在 C 语言源程序中允许用一个标识符来表示一个字符串,称为“宏” ,被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的宏名,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。 宏定义是由源程序中的宏定义命令完成的,宏代换是由预处理程序自动完成的。在 C 语言中,宏分为 有参数和无参数两种。无参宏的宏名后不带参数,其定义的一般形式为: 

#define 标识符 字符串; 

其中“#”表示这是一条预处理命令(在 C 语言中凡是以“#”开头的均为预处理命令)“define”为宏定义命令,“标识符”为所定义的宏名, “字符串”可以是常数、表达式、格式串等。符号常量的定义就是一种无参宏定义。 
此外,常常对程序中反复使用的表达式进行宏定义。例如: 

#define M (y*y+3*y);

它的作用是指定标识符 M 来代替表达式(y*y+3*y)。 
在编写源程序时, 所有的(y*y+3*y)都可由 M 代替, 而对源程序进行编译时,将先由预处理程序进行宏代换,即用(y*y+3*y)表达式去置换所有的宏名 M,然后 再进行编译。 
C语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对于带 参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。 带参宏定义的一般形式为: 

#define 宏名(形参表) 字符串; 

在字符串中含有各个形参。 
带参宏调用的一般形式为: 

宏名(实参表); 

例如: 

1 #define M(y) y*y+3*y
2 /*宏定义*/
3 ......
4 k=M(5);
5 /*宏调用*/

在上面的宏调用时,用实参 5 去代替形参 y,经预处理宏展开后的语句为: 
k=5*5+3*5; 
程序 2.26 给出了一个宏定义和调用的完整实例。 
定义一个名为 MAX 的带参数的宏,可以通过它来选出参数 a、b 中的较大值:test26.c。 

 1 #include <stdio.h>
 2 #define MAX(a,b) (a>b)?a:b
 3 /*带参数的宏定义*/
 4 main()
 5 {
 6 int x,y,max;
 7 printf("input two numbers: ");
 8 scanf("%d %d",&x,&y);
 9 max=MAX(x,y);
10 printf("max=%d
",max);
11 /*宏调用*/
12 }</stdio.h>

程序运行结果如下(□表示空格,↙表示回车) :     

1 input two numbers: 200920102 max=2010

可以看到,宏替换相当于实现了一个函数调用的功能,而事实上,与函数调用相比,宏调用更能提高C 程序的执行效率。

二、文件包含

文件包含 
文件包含是 C 预处理程序的另一个重要功能,文件包含命令行的一般形式为: 

#include "文件名" 

或者 

#include <文件名> 

文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源 
程序文件连成一个源文件。 
在程序设计中,文件包含是很有用的。一个大的程序可以分为多个模块,由多个程序员分别编程,有些公用的符号常量或宏定义等可单独组成一个文件,在其他文件的开头用包含命令包含该文件即可使用。 这样,可避免在每个文件开头都去书写那些公用量,从而节省时间,并减少出错。这里对 C 语言的文件包含命令进行以下几点说明: 
(1)包含命令中的文件名可以用双引号引起来,也可以用尖括号引起来。例如以下写法都是允许的: 

1 #include "stdio.h"
2 #include <stdio.h>

但是这两种形式是有区别的:使用尖括号表示在包含文件目录中去查找(包含目录是由系统的环境变 量进行设置的,一般为系统头文件的默认存放目录,比如 Linux 系统在/usr/include 目录下),而不在源文件的存放目录中查找; 使用双引号则表示首先在当前的源文件目录中查找, 若未找到才到包含目录中去查找。用户编程时可根据自己文件所在的目录来选择某一种命令形式。 
(2)一个 include 命令只能指定一个被包含文件,若有多个文件要包含,则需用多个 include 命令。 
(3)文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。 

三、条件编译

条件编译 
预处理程序提供了条件编译的功能,可以按不同的条件去编译不同的程序部分,因而产生不同 的目标代码文件,这对于程序的移植和调试是很有用的。条件编译可分为三种形式。 
第一种形式如下: 

1 #ifdef 标识符 
2 程序段 1 
3 #else 
4 程序段 2 
5 #endif 

它的功能是如果标识符已被 #define 命令定义过则对程序段 1 进行编译;否则对程序段 2 进行编译。 
如果没有程序段 2(为空),本格式中的#else 可以没有,即可以写为: 

1 #ifdef 标识符 
2 程序段 
3 #endif 

第二种形式如下: 

1 #ifndef 标识符 
2 程序段 1 #else 
3 程序段 2 #endif 

与第一种形式的区别是将“ifdef”改为“ifndef”。它的功能是如果标识符未被#define 命令定义过则对程 序段 1 进行编译,否则对程序段 2 进行编译。这与第一种形式的功能正好相反。 
第三种形式如下: 

1 #if 常量表达式 
2 程序段 1 #else 
3 程序段 2 #endif 

它的功能是如果常量表达式的值为真(非 0),则对程序段 1 进行编译,否则对程序段 2 进行编译。 
因此可以使程序在不同的条件下完成不同的功能。

四、其他与处理命令

#error 等其他常用预处理命令 
除了上面介绍的之外,C 语言还有#erroe、#line、#pragma 等其他常用的预处理命令,在很多 C 语言的 
程序中也是经常可见的。下面向读者简单介绍一下它们。 

1. #error 
#error 指令强制编译程序停止编译,它主要用于程序调试。#error 指令的一般形式是: 

#error error-message 
注意,宏串 error-message 不用双引号引起来。遇到#error 指令时,错误信息被显示,可能同时还显示 
编译程序作者预先定义的其他内容。 

2. #line 
#line 指令改变__LINE__和__FILE__的内容。__LINE__和__FILE__都是编译程序中预定义的标识符。 __FILE__的内容是当前被编译源文件的文件名。 
#line标识符__LINE__的内容是当前被编译代码行的行号,其一般形式是: 

#line number “filename” 
其中,number 是正整数并变成__LINE__的新值; 可选的“filename”是合法文件标识符并变成__FILE__的新值。#line 主要用于调试和特殊应用。 

3. #pragma 
#pragma 是编译程序实现时定义的指令,它允许由此向编译程序传入各种指令。例如,一个编译程序可能具有支持跟踪程序执行的选项,此时可以用#pragma 语句选择该功能,编译程序忽略其不支持的#pragma 选项。使用#pragma 预处理命令可提高 C 源程序对编译程序的可移植性。 

原文地址:https://www.cnblogs.com/strengthen/p/11418890.html