TypechoJoeTheme

日志随记

统计
登录
用户名
密码

扫码登录
/
注册
用户名

此用户名将作为登录时所用的账号

邮箱

XG.孤梦

随风而动,随遇而安......

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

XG.孤梦博 主大佬
2022-02-15
/
0 评论
/
876 阅读
/
1876 个字
/
百度已收录
02/15
本文最后更新于 2022年08月13日,已超过 616天没有更新。如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!

预处理命令概述

  • 所谓编译预处理就是在编译程序对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)
  • 符号常量

    • 在定义无参宏时,如果宏定义中的“字符串”是一个常量,则相应的““宏名” 称为“符号常量”。
  • 例子:
#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)
  • 有参宏的调用和宏展开

    • 调用格式:宏名(实参表)
    • 宏展开:用宏调用提供的实参字符串,直接替换宏定义命令行中相应形参字符串,非形参字符保持不变。
  • 例子:
#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.在宏定义中,字符串内的形参和整个表达式通常要用括号括起来以避免出错。
  • 例子:
# 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 作为一个源文件单位进行编译

条件编译

  • 如果希望程序中的一部分只在满足一定条件时才进行编译,也就是对这部分内容指定编译的条件,可以使用条件编译实现。
  • 条件编译有以下几种形式:

    • 形式一
#ifdef 标识符
    程序段1
#else
    程序段2
#endif

// 或者

#ifdef 标识符
    程序段1
#endif
  • 功能:若标识符是已被宏定义指令定义过的宏名,则只对程序段1进行编译,程序段2不参加编译;
    否则只对程序段2进行编译,程序段1不参加编译。

  • 形式二
#ifndef 标识符  // if n def
    程序段1
#else
    程序段2
#endif
  • 功能:若标识符是未被宏定义指令定义过的宏名,则只对程序段1进行编译,程序段2不参加编译;
    否则只对程序段2进行编译,程序段1不参加编译。与形式一刚好相反

  • 形式三
#if 常量表达式
    程序段1
#else
    程序段2
#endif
  • 功能:如常量表达式的值为真(非0),则对程序段1进行编译,否则对程序段2进行编译。
    因此可以使程序在不同条件下,完成不同的功能。
  • 例子
#include <stdio.h>
#define DEBUG 0

void main()
{
    #if DEBUG
        printf("Debugging...\n");
    #else
        printf("Running...\n");
    #endif
}

  • #define DEBUG 1

  • 上面介绍的条件编译当然也可以用条件语句来实现。
    但是用条件语句将会对整个源程序进行编译,生成的目标代码程序很长;
  • 而采用条件编译,则根据条件只编译其中的程序段1或程序段2,生成的目标程序较短。
    如果条件选择的程序段很长,采用条件编译的方法是十分必要的。
  • 有利于程序的可移植性,增加程序的灵活性。

学习笔记C语言
朗读
赞(0)
赞赏
感谢您的支持,我会继续努力哒!
版权属于:

日志随记

本文链接:

https://www.xggm.top/archives/569.html(转载时请注明本文出处及文章链接)

评论 (0)
 
XG.孤梦博 主大佬

随风而动,随遇而安......


访问 109789 ℃
51 文章数
115 评论量

标签云

登录
X
用户名
密码