【高级语言程序设计(一)】第 9 章:编译预处理命令

目录

前言

一、宏定义命令

(1)无参宏定义

(2)有参宏定义

① 带参数的宏定义

②  带参宏定义与函数的区别

二、文件包含命

(1)文件包含命令的定义

(2)文件包含命令的格式

(3)“文件包含” 处理

三、条件编译命令

(1)条件编译命令的定义

(2)条件编译命令的格式

① 格式一:#ifdef

② 格式二:#ifndef

③ 格式三:#if 



前言

编译预处理命令:

编译预处理 是指对源程序进行编译之前,先对源程序中的各种预处理命令进行处理;然后再将处理结果和源程序一起进行编译,以获得目标代码。


一、宏定义命令

C 语言包括两种宏定义命令,即 无参宏定义命令 有参宏定义命令
通常,宏定义用于定义程序中的符号常量、类型别名、运算式代换和语句代换等。

(1)无参宏定义

【格式】 #define 宏名 字符序列
【说明】
  1. “宏名” 是标识符的一种,命名规则与标识符相同,通常采用大写字母,以便与变量区分
  2. “字符序列” 可以是常量、表达式各种符号等。
  3. “宏名” 和 “字符序列” 之间使用空格符分隔。
  4. 宏定义不是 C 语言的语句,所以不能在行尾加分号。否则,宏展开时会将分号作为字符串的一个字符,用于替换宏名。
  5. 宏展开时,预处理程序仅按宏定义简单地替换宏名,而不做任何检查。
  6. 对于双引号括起来的字符串内的字符,即使与宏名重名,也不进行宏展开。
  7. 宏定义命令 #define 出现在函数的外部,宏名的作用域是从宏定义开始到本文件结束。通常,宏定义命令放在文件的开头。
  8. 宏定义时,可以引用已定义的宏名,宏展开是逐层替换的。

【优点】

  1. 提高源程序的可读性
  2. 提高源程序的可修改性,修改宏定义中的“字符序列”可以起到一改全改的作用
  3. 避免源程序中重复书写字符串

(2)有参宏定义

【格式】 #define  宏名(形参表)  带参数的字符序列
【有参宏定义的调用和宏展开格式 宏名(实参表)
【宏展开的过程
  1. 若程序中有带实参的宏,则按照 #define 指定的字符序列从左至右进行替换
  2. 若宏定义中包含形参,则用实参直接替换宏定义命令行中相应的形参,其中实参可以是常量、变量或表达式
  3. 若宏定义的字符序列中的字符是非形参字符则替换时保留不变

【说明

  1. 定义有参宏时,宏名与左圆括号之间不能留有空格。否则,C 编译程序将空格以后的所有字符均作为替代字符串,而将该宏视为无参宏。
  2. 有参的宏展开只是将实参作为字符串,简单地替换形参字符串,而不进行任何语法检查。

① 带参数的宏定义

【格式】 #define  宏名(参数表)  宏体
【注意】“宏体” 不能加空格
【功能】进行字符串替换,并进行参数替换
【示例

 【说明

  1. 宏展开:形参用实参换,其它字符保留
  2. 宏体及各形参外一般应加括号 ()

/* 示例:使用带参数的宏 */
#include <stdio.h>
#define PI 3.1415926
#define S(r) PI*r*r
void main()
{ 
  float a,area;
  a=3.6; area=S(a);
  printf("r=%f\narea=%f\n", a, area);
}

/* 运行结果 */
r=3.600000
area=40.715038

②  带参宏定义与函数的区别

【区别】

  • 函数调用时,先求实参表达式的值,再带入形参。 宏只进行简单字符替换,不求值。
  • 函数调用在程序运行时处理和分配临时内存单元。 宏展开在编译时进行,不分配内存单元,无值传递和返值。
  • 函数要定义形实参且类型一致,宏无类型,其参数无类型。
  • 函数只有一个返回值,宏可以设法得到几个结果。
  • 宏展开使源程序变长,函数调用源程序不变长。
  • 宏替换不占运行时间,只占编译时间。 函数调用占运行时间。

【示例】

/* 示例:使用宏带回几个结果 */
#include <stdio.h>
#define PI 3.1415926
#define CIRCLE(R,L,S,V)  L=2*PI*R;S=PI*R*R;
                         V=4.0/3.0*PI*R*R*R
void main()
{ 
  float r, l, s, v;
  scanf("%f", &r);
  CIRCLE(r, l, s, v);
  printf("r=%6.2f, l=%6.2f, s=%6.2f, v=%6.2f\n", r, l, s, v);
} 

/* 宏展开后 */
void main()
{ 
  float r, l, s, v;
  scanf("%f", &r);
  l=2*3.1415926*r; s=3.1415926*r*r; v=4.0/3.0*3.1415926*r*r*r;
  printf("r=%6.2f, l=%6.2f, s=%6.2f, v=%6.2f\n", r, l, s, v);}


/* 运行结果 */
3.5
r=3.50, l=21.99, s=38.48, v=179.59

/* 示例 1 :用宏定义和函数实现同样的功能 */
#define MAX(x,y) (x)>(y)?(x):(y) 
...
main()
{ 
  int a,b,c,d,t; 
  ...
  t=MAX(a+b,c+d); 
  ...
}

/* 宏展开后 */
t=(a+b)>(c+d)?(a+b):(c+d);


/* 示例 2 :用宏定义和函数实现同样的功能 */
int max(int x, int y)
{ 
  return(x>y?x:y);
}

main()
{ 
  int a, b, c, d, t; 
  ...
  t=max(a+b,c+d); 
  ...
}
/* 示例 1 :用宏代表输出格式 */
#include <stdio.h>
#define PR printf
#define NL "\n"
#define D "%d"
#define D1 D NL
#define D2 D D NL
#define D3 D D D NL
#define D4 D D D D NL
#define S "%s"

/* 示例 2 :用宏代表输出格式 */
void main()
{ 
  int a, b, c, d;
  char string[]="CHINA";
  a=1; b=2; c=3; d=4;
  PR(D1, a);
  PR(D2, a, b);
  PR(D3, a, b, c);
  PR(D4, a, b, c, d);
  PR(S, string);
}


/* 运行结果 */
1
12
123
1234
CHINA


二、文件包含命

(1)文件包含命令的定义

  1. 文件包含是指一个源文件可以将另一个源文件的全部内容包含进来。
  2. 文件包含命令可以将多个模块共用的数据(如符号常量和数据结构)或函数,集中到一个单独的文件中。

(2)文件包含命令的格式

【格式】
  1. #include “文件名”
  2. #include <文件名>
【区别】 上述 2 种格式的区别仅在于:
  • 格式 1,系统首先到当前源文件所在的目录查找被包含文件,如果没有找到,再到系统指定的 “包含文件目录” 去查找,必要时在文件名前加上所在的路径。
  • 格式 2 ,直接到系统指定的 “包含文件目录” 去查找。
【说明】
  1. 编译预处理时,预处理程序将查找指定的被包含文件,并将其复制到 #include 命令出现
  2. 的位置上。
  3. 文件头部的被包含文件称为 “标题文件” 或 “头部文件” ,常以 “h”(head)作为扩展名,简称头文件。
  4. 一条文件包含命令,只能指定一个被包含文件。
  5. 由于库函数以及某些宏定义都存放在系统文件中,一般都存放在系统指定的路径下,所以将存放库函数的头文件包含在源程序时一般建议采用格式二,即 #include <文件名>。
  6. 文件包含命令允许嵌套,即一个被包含的文件中可以再使用文件包含命令包含另一个文件,而在该文件中还可以再包含其他文件。
  7. 若被包含文件的内容发生变化,则应该对包含此文件的所有源文件重新进行编译。

(3)文件包含” 处理

【一般形式】
  1. #include “文件名”
  2. #include <文件名>

【区别】上述 2 种格式的区别仅在于:

  • < >  直接按标准目录搜索
  • " " 先在当前目录搜索,再搜索标准目录可指定路径
【功能】一个源文件可将另一个源文件的内容全部包含进来
【处理过程】
  • 预编译时用被包含文件的内容取代该预处理命令,再将 “包含” 后的文件作为一个源文件单位进行编译,得目标文件 .obj



三、条件编译命令

(1)条件编译命令的定义

C 语言提供了条件编译命令,使得用户可以选择对源程序的一部分内容进行编译,即不同的编译条件产生不同的目标程序。

(2)条件编译命令的格式

① 格式一:#ifdef

【格式】
#ifdef 标识符
  程序段1
[#else
  程序段2]
#endif

【功能】

  • 当 “标识符” 已经被 #define 命令定义,则编译程序段1;否则编译程序段2。
  • 其中 [] 中的内容,即 #else 部分可以省略,即:
    #ifdef 标识符
      程序段1
    #endif

② 格式二:#ifndef

【格式】
#ifndef 标识符
  程序段1
[#else
  程序段2]
#endif

【功能】

  • 当 “标识符” 没有被 #define 命令定义,则编译程序段1;否则编译程序段2。
  • 其中 [] 中的内容,即 #else 部分可以省略,即:
    #ifndef 标识符
      程序段1
    #endif

③ 格式三:#if 

【格式】
#if 标识符
  程序段1
[#else
  程序段2]
#endif

【功能】

  • 当表达式为 “真”(非 0 )时,则编译程序段1;否则编译程序段2。
  • 其中 [] 中的内容,即 #else 部分可以省略,即:
    #if 标识符
      程序段1
    #endif

【注意】

  • 其中的 “表达式” 是在编译阶段计算值的,所以此处的 “表达式” 不能是变量,必须是常量或用宏定义命令 #define 定义的标识符。

猜你喜欢

转载自blog.csdn.net/qq_39720249/article/details/130872403