zoukankan      html  css  js  c++  java
  • 预处理命令

      ANSI C 标准规定可以在C源程序中加入一些 预处理命令,以改进程序设计环境,提高编程效率,这此预处理伪政府是由ANSI C 统一规定的,但是它不是C语言本身的组成部分,不能直接对它们进行编译。

      C提供的预处理命令功能主要有以下3种:

      (1)宏定义

      (2)文件包含

      (3)条件编译

      分别用宏定义伪命令,文件包含命令,条件编译命令来实现,为了与一般C语句相区别,这些命令以符号 # 开关。

    1 宏定义

    1.1 不带参数的宏定义

      用一个指定的标识符(即名字)来代表一个字符串,它的一般形式为

      #define 标识符 字符串

    如:

      #define PI 3.1415926

      它的作用是指标识符PI来代替 3.1415936 这个字符串,在编译预处理时,将程序中在该命令以后出现的所有的PI都用 3.1415926 代替。这种方法使用户能以一个简单的名字代替一个长的字符串,因此把这个标识符(名字)称为(宏名)在预编译时将宏名替换成字符串的过程称为 宏展开 。#define 是宏定义命令。

    说明:

      (1)宏名一般习惯用大写字母表示,以便与变量名相区别,但这并非规定,也可以用小写。  

      (2)使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量。

    如:

      #defiine array_size 1000

      int aray[aray_size];

      如果要改变数组大小只要修改宏定义,使用宏定义可以提高程序的通用性

      (3)宏定义是用宏名代替字符串,也就是做简单的置换,不作正确性检查。

    如:

      #define PI 3.14l59

      即把字符l写成数字1预处理也照样代入,不管含义是否正确,也就是预编译时不会作任何语法检查,只有在编译已被宏展开后的源程序时发会发现错误并报错。

      (4)宏定义不是C语句,不必要行末加分号,如果加了分号也可连分号一起进行置换。

    如:

      #define PI 3.1415926

      arre = PI * r * r;

      经过展开后得到

      area = 3.1415926; * r * r;

      显然出现语法错误。

      (5)#define 命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到本源文件结束,通常 #define 命令写在文件开头,函数之前,作为文件一部分,在此文件范围内有效。

      (6)可以用 #undef 命令终止宏定义的作用域。

    如:

    #define G 9.8

    void main()

    {

      ......
    }

    #undef G

    f()

    {

      .....
    }

      由于#undef 的作用,使G的作用范围在 #undef 行处终止,因些在f函数中,G不再表9.8。

    1.2 带参数的宏定义

      不是进行简单的字符串替换,还要进行参数替换,其定义的一般形式为

    #define 宏名(参数表) 字符串

      字符串中包含在括弧中所指定的参数如:

      #define S(a, b) a * b

      area = S(3, 2);

      因此展开得到

      area = 3 * 2;

      对带参数的宏定义是这样展开置换的,在程序中有带实参的宏如 S(3, 2) 则按#define 命令行中指定的字符串从左到右进行置换,如果串中包含宏中的形参,如a,  b 则将程序语句中的相应的实参(可以是常量,变量或表达式)代替形参,如果宏定义中的字符中的字符不是参数字符 如 (a * b 中的 * 号)则保留,这样就形成了置换字符串。

    如:

    #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("area = %f\n", area);
    }

    运行结果:area = 40.715038

    area = S(a); 经宏展开后为

    area = 3.1415926 * a * a;

    说明:

      (1)对带参数的宏的展开只是将语句中的宏名后面括号内的实参字符串代替 #define 命令行中的形参,如语句中有 S(a) 在展开时找到 #define 命令行中的 S(r)将S(a)中的实参a代替宏定义中的字符串 PI * r * r 中的形参 r 得到 PI * a * a,这是容易理解而且不会发生问题,但是如果有以下语句:

      area = S(a + b);

      这时把实参 a + b 代替 PI * r * r 中的形参 r 成为

      area = PI * a + b * a + b;

      请注意在 a + b 外面没有括弧,显然这与程序设计都的愿意不符,愿意希望得到

      area = PI * (a + b) * (a + b);

      为了得到这个结果,应当在定义时,在字符串中的形式参数外面加一个括号即:

      #define S(r) PI * (r) * (r)

      (2)在宏定义时,在宏名与带参数的括弧之间不应加空格,否则将空格以后的字符都作为替代字符串的一部分。

      带参数宏与函数区别

      a.函数调用时先求出实参表达式的值,然后代入形参,面使用带参数的宏只是进行简单的字符替换。

      b.函数调用是在程序运行时处理的,分配临时的内存单元,而宏展开是在编译时进行的,在展开时并不分配内存单元,不进行值的传递处理,也没有返回值的概念。

      c.对函数中的实参和形参都要定义类型,二者的类型要求一致,如果不一致应进行类型转换,而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个符号代表展开时代入指定的字符即可。宏定义时字符串可以是任何类型的数据。

    如:

      #define CHARI CHINA

      #define a 3.6

      d.调用函数只可以得到一个返回值,而用可以设法得到几个结果。

    如:

    #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);
    }

      e.使用宏次数多时,宏展开后源程序长,因为每展开一次都使程序增长,而函数调用不使源程序变长。

      f.宏替换不占运行时间,只占编译时间,而函数调用则占用运行时间(分配单元,保留现场,值传递,返回)。

      一般用宏来代表简短的表达式比较合适,如:

      #define MAX(x, y) (x)>(y)?(x):(y)

    2 文件包含处理

      所谓文件包含处理是指一个源中以将另外一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。C语言提供了 #include 命令来实现 文件包含的操作。其一般形式为:

      #include "文件名" 或 #include <文件名>

      文件包含命令很有用,它可以节省程序设计人员的重复劳动。

      (1)一个include 命令只能指定一个被包含的文件,如果要包含n个文件,要用n个include 命令。

      (2)如果文件1包含文件2而文件2中要用到文件3的内容,则可以在文件1中用两个include 命令分别包含文件2和文件3,而且文件3应出现在文件2前即:在file1.c中定义

      #include "file3.h"

      #include "file2.h"

      这样file1 file2都可以用file3的内容,在file2中不必再用 #include <file3.h>

      (3)在一个被包含文件中又可以包含另一个被包含文件,即文件包含是可以嵌套的。

      (4)在 #include 命令中,文件名可以用双引号或尖括号。

    如:#include <file2.h> 或 #include "file2"

      二者的区别,用尖括弧时,系统到存放C库函数头文件所在目录寻找要包含的文件,这称为标准方式,双引号时,系统先在用户当前目录中寻找要包含的文件,若找不到,再按标准方式查找。一般来说,如果为调用库函数在尖括号以节省查找时间,如果要包含是用户自己编写的文件,一般用双引号。

      (5)被包含文件(file2.h)与其所在的文件(即用 #include 命令的源文件file.c)在预编译后已成为同一个文件,(而不是两个文件)因此如果file2.h中有全局静态变量它也在file1.c文件中有效,不必用extern声明。

    3 条件编译

      一般情况下,源程序中所有的行都参加编译,但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是 条件编译,有时希望当满足条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。

      (1)#ifdef 标识符

          程序段1

         #else

          程序段2

         #endif

      它的作用所指定的标识符已经被 #defiine 命令定义过,则在程序编译阶段只编译程序段1,否则编译程序段2,其中#else 部分可以没有即

      #ifdef 标识符

        程序段1

      #endif

      这里的程序段可以是语句组,也可以是命令行,这种条件编译对于提高C源程序的通用性是很有好处的。如果一个C源程序在不同计算机系统上运行,而不同的计算机又有一定的差异,(有的机器以16位来存放一个整数,而有的则以32位存放一个整数)这样往往需要对源程序作必要的修改,这就降低了程序的通用性,可以用以下的条件编译来处理:

      #ifdef COMMPUTER_A

      #define INTEGER_SIZE 16

      #else

      #define INTEGER_SIZE 32

      #endif

      即如果 COMPUTER_A 在前面已被定义过,则编译下面的命令行

      #define INTEGER_SIZE 16

      否则编译下面的命令行

      #define INTEGER_SIZE 32

      如果在这组条件编译命令之前曾出现以下命令行:

      #define COMPUTER_A 0

      或将 COMPUTER_A 定义为任何字符中甚至是

      #define COMPUTER_A

      则编译后程序中的INTEGER_SIZE都用16代替,否则用32代替。

      这样,源程序可以不必作任何修改就可以用于不同类型的计算机系统。

      (2)#ifndef 标识符

          程序段1

         #else

          程序段2

         #endif

      与第一个种形式只是在第一行不一样,#ifndef 它的作用是若标识符末被定义过则编译程序段1,否则编译程序2,这种形式与第一种形式的作用相反。

      (3)#if 表达式

          程序段1

         #else

          程序段2

         #endif

      它的作用是,当指定的表达式值为真,时就编译程序段1,否则就编译程序段2。可以事先给定一定条件,使程序在不同的条件下执行不同的功能。

  • 相关阅读:
    LeetCode44——用搜索的思路去理解动态规划算法
    在vscode中配置LeetCode插件,从此愉快地刷题
    分布式专题——详解Google levelDB底层原理
    高等数学——求解不定积分经典换元法
    动态规划入门——详解完全背包与多重背包问题
    详解聚类算法Kmeans的两大优化——mini-batch和Kmeans++
    Python专题——详解enumerate和zip
    LeetCode42题,单调栈、构造法、two pointers,这道Hard题的解法这么多?
    零基础学Python--------第11章 使用Python操作数据库
    零基础学Python--------第10章 文件及目录操作
  • 原文地址:https://www.cnblogs.com/qkhhxkj/p/2090304.html
Copyright © 2011-2022 走看看