前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言预处理指令-学习二十一

C语言预处理指令-学习二十一

作者头像
XG.孤梦
发布2022-03-01 14:15:38
5340
发布2022-03-01 14:15:38
举报
文章被收录于专栏:日志随记日志随记

本文最后更新于2022年02月15日,已超过12天没有更新。如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!

预处理命令概述

  • 所谓编译预处理就是在编译程序对C源程序进行编译前,由编译预处理程序对这些编译预处理指令行进行处理的过程。
  • C语言中,以 “#” 开头的行,都称为编译预处理指令行,每行的末尾 没有“;”
  • C提供的预处理功能主要有以下3种:
    • 宏定义
    • 文件包含
    • 条件编译

宏定义

无参宏

  • 无参宏的定义格式:
    • #define 标识符 字符串
    • define 为宏定义命令。
    • 标识符 为所定文的宏名,通常用大写字母表示,以便于与变量区别。
    • 字符串 可以是常数、表达式等。
  • 例如:
    • #define PAI 3.1415926
  • ”:用一个标识符来表示一个字符串
  • 宏名”:被定义为“”的标识符
  • 宏替换”:在编译预处理时,预处理程序将程序中所有出现的“宏名”,都用宏定义中的字符串去替换。 完成后,才将程序交给编译程序去处理。
  • 使用宏定义的优点:
    • 可提高源程序的可维护性;
    • 可提高源程序的可移植性;
    • 减少源程序中重复书写字符串的工作量。
  • 关于宏定义几点说明:
    • 宏名一般用大写字母表示,以示与变量区别。但这并非是语法规定
    • 宏定义不是C语句,所以不能在行尾加分号
    • 在宏展开时,预处理程序仅按宏定义简单替换宏名,而不作任何检查。
    • 宏定义命令#define出现在函数的外部,宏名的有效范围是:从定义命令之后,到本文件结束
    • 在进行宏定义时,可以引用已定义的宏名。
      • 例如:
      • # defineR 3
      • # define PI 3.14159
      • # define S PI*R*R
  • 对双引号括起来的字符串内的字符,即使与宏名同名,也不进行宏展开。
    • 例如: printf("R=%f,S=%f",R,S)
  • 符号常量
    • 在定义无参宏时,如果宏定义中的“字符串”是一个常量,则相应的““宏名” 称为“符号常量”。
  • 例子:
代码语言:javascript
复制
#include<stdio.h>
#define A 1+2   // 没有括号

void main() {
    int a;
    a = A * 2;   // 替换后 a = 1+ 2 * 2 所以a = 5

    printf("a = %d,A = %d\n", a, A);
}
有参宏
  • 有参宏的定义格式:
    • #define 宏名(参数表) 字符串
  • 例如:
    • #define ADD(X,Y) (X+Y)
  • 有参宏的调用和宏展开
    • 调用格式:宏名(实参表)
    • 宏展开:用宏调用提供的实参字符串,直接替换宏定义命令行中相应形参字符串,非形参字符保持不变。
  • 例子:
代码语言:javascript
复制
#include<stdio.h>
#define PAI 3.1415926
#define S(r) PAI*(r)*(r)

void main() {
    double a;
    printf("请输入半径:\n");
    scanf_s("%lf", &a);
    printf("半径为%.2f的圆的面积:%.2f\n", a, S(a));
    
}
  • 关于有参宏的几点说明:
  • 1.定义有参宏时,宏名与左圆括号之间不能留有空格
    • #define S(r) PAI*(r)*(r)
    • 上例中在 S 和 (r) 之间,不能有空格。 如果写成了 #define S (r) PAI*(r)*(r)
    • 表示 宏名S 所替换的字符串为 (r) PAI*(r)*(r)
  • 2.有参宏定义中,形参不分配内存单元,因此形参不必做类型定义; 而宏替换中的实参有具体的值要用它们去代换形参,因此实参必须做类型说明
    • 在有参宏中,只是符号替换
  • 3.调用有参宏名时,一对圆括号必不可少,圆括号中实参的个数应该与形参个数相同, 如果有多个参数,参数之间用逗号隔开。
  • 4.在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。 宏替换时对实参表达式不做计算直接照原样替换
  • 5.在宏定义中,字符串内的形参和整个表达式通常要用括号括起来以避免出错。
  • 例子:
代码语言:javascript
复制
# include< stdio.h >
# define A(X,Y) X*Y

void main()
{
    int a, s;
    float w;

    printf("请输入a的值:\n");
    scanf_s("%d", &a);
    s = A(a, a + 1);
    w = 6 / A(a, a);

    printf("s = a*a+1 = %d,w = 6/a*a = %.2f\n", s, w);
}
  • 写成如下形式: #define A(X,Y) (X)*(Y)
  • 第1次调用宏计算 s 值时,宏替换后的语句: s = ((a)*(a+1));
  • 第2次调用么计算 w 值时,宏替换后的语句: w = 6/((a)*(a));
  • 结果就是 s = ((a)*(a+1)) = 42, w = 6/((a)*(a))= 6.00
  • 宏定义时应在参数两侧加括号,也应在整个字符串外加括号

文件包含

  • 文件包含是指在一个文件中,去包含另一个文件的全部内容。 C语言用#include指令实现文件包含的功能。
  • 文件包含的语法格式:
    • 首先在源码当前目录下面寻找该头文件,此方法通常用于包含自己定义的头文件。 #include "文件名"
    • 首先在编译器默认的include目录下寻找该头文件,此方法通常用于包含标准库头文件。 #include <文件名>
  • 例如:
    • #include <stdio.h>
    • #include <math.h>
    • #include "diy.h"
  • 在编译预处理阶段,预处理程序将用指定文件中的内容来替换此行。 从而把指定的文件和当前的源程序文件连成一个源文件。
  • 如果程序很大,最好分为几个不同的文件,每个文件含有一组函数。这些文件用#include将它们包含在主程序的开始处。
  • 有一些函数和宏几乎所有的程序中都会用到。可以将这些常用函数和宏定义存放在一个文件中, 将这个文件包含在所写的程序中,该文件的内容就会插到程序中
  • 被包含的文件扩展名可以为 .h ,此扩展名为头文件一般包含在程序的头部
  • 所有库函数被分成不同的类别,存放于不同的文件中。
  • 使用文件包含命令时要注意以下几点:
    • 1.当被包含文件修改后,包含该文件的源程序必须重新进行编译连接。
    • 2.一个文件包含命令只能指定一个被包含文件,如果要包含多个文件,则应使用多个文件包含命令。
      • #include <stdio.h>
      • #include <string.h>
      • #include <math.h>
  • 3.文件包含允许嵌套,即在一个被包含的文件中又可包含另一个文件。
  • 在编译预处理时,要对 #include 命令进行”文件包含”处理,将 f2.c 的全部内容插入到 #include"f2.c" 命令处,得到所示的结果.在编译时,对 f1.c 作为一个源文件单位进行编译

条件编译

  • 如果希望程序中的一部分只在满足一定条件时才进行编译,也就是对这部分内容指定编译的条件,可以使用条件编译实现。
  • 条件编译有以下几种形式:
    • 形式一
代码语言:javascript
复制
#ifdef 标识符
    程序段1
#else
    程序段2
#endif

// 或者

#ifdef 标识符
    程序段1
#endif
  • 功能:若标识符是已被宏定义指令定义过的宏名,则只对程序段1进行编译,程序段2不参加编译; 否则只对程序段2进行编译,程序段1不参加编译。
  • 形式二
代码语言:javascript
复制
#ifndef 标识符  // if n def
    程序段1
#else
    程序段2
#endif
  • 功能:若标识符是未被宏定义指令定义过的宏名,则只对程序段1进行编译,程序段2不参加编译; 否则只对程序段2进行编译,程序段1不参加编译。与形式一刚好相反
  • 形式三
代码语言:javascript
复制
#if 常量表达式
    程序段1
#else
    程序段2
#endif
  • 功能:如常量表达式的值为真(非0),则对程序段1进行编译,否则对程序段2进行编译。 因此可以使程序在不同条件下,完成不同的功能。
  • 例子
代码语言:javascript
复制
#include <stdio.h>
#define DEBUG 0

void main()
{
    #if DEBUG
        printf("Debugging...\n");
    #else
        printf("Running...\n");
    #endif
}
  • #define DEBUG 1
  • 上面介绍的条件编译当然也可以用条件语句来实现。 但是用条件语句将会对整个源程序进行编译,生成的目标代码程序很长;
  • 而采用条件编译,则根据条件只编译其中的程序段1或程序段2,生成的目标程序较短。 如果条件选择的程序段很长,采用条件编译的方法是十分必要的。
  • 有利于程序的可移植性,增加程序的灵活性。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-02-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 预处理命令概述
  • 宏定义
    • 无参宏
      • 有参宏
  • 文件包含
  • 条件编译
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档