• 回忆:#define的用法


    ANSI C规定:#前可以有空格或者tab,#和指令其余部分之间也可以有空格,可以出现在任何地方,作用域从定义处到文件结尾。

    因为预处理开始前,系统会删除反斜线和换行符的组合,故可以把指令扩展到几个物理行,这些物理行组成单个逻辑行。

    //每个#define行(指逻辑的行):三部分组成
    //指令本身   宏   替换列表(或主体)
    #define     PI    3.141592653

    宏分为类对象宏(代表值的宏)和类函数宏
    宏的名字中间不能有空格,必须遵循c命名规则,从宏变成最终的替换文本叫宏展开,预处理器不进行计算,只是简单的文本替换

    语言符号类型字符串和字符型字符串

    系统把主体当作语言符号类型字符串,而不是字符型字符串,预处理器中的语言符号是宏定义主体里的单独的词,用空白字符把这些词分开。

    #define SIX 2*2//定义里有一个语言符号,序列2*2
    #define AAA 2     *     2//定义里有三个语言符号,2,*,3

    若把主体解释为字符型字符串,预处理器用2    *     2替换AAA,额外的空格也算替换

    若把主体解释为语言符号类型字符串,预处理器只用单个空白字符分割的三个语言符号去去替换AAA

    2 * 2的空格只是分割主体语言符号的符号,不会都算(注意:不同编译器做法不一样

    重定义常量

    开始把AAA定义为常量4,后来在该文件里,又把AAA定义为10,这叫重定义常量,不同编译器策略不一样,不过标准c规定:这是错误的,只允许新定义和旧定义完全相同!

    //相同定义意味主体具有相同顺序的语言符号
    #define A 2 * 3
    #define A 2    *   3//两者等价,都是三个语言符号的主体

    下面的定义被ANSI C认为不同

    #define A 2*3//只有一个语言符号的主体,可以用#undef指令重新定义宏

    在#define里使用参数——类函数宏

    类函数宏:外形和作用和函数类似,可以使用参数,也是()括起来。一般要主体参数里都加上小括号来避免文本替换的错误发生。

    #define  SQRT(x)  x*x
    int main(void)
    {
        int x = 2;
        printf("%d
    ", SQRT(2));//4
        printf("%d
    ", SQRT(4));//16
        printf("%d
    ", SQRT(2 + x));//8
        printf("%d
    ", SQRT(2 + 2));//没有打印16,而是8=2 + 2 * 2 + 2
        //一定主要,类函数宏也是文本替换,可以通过在主体里加圆括号来约束结合性
        
      //避免在宏里使用增量和减量运算符
        printf("%d
    ", SQRT(++x));//16,++x*++x,x两次增量,一次在乘法前,一次在后
        printf("%d
    ", x);//4
        //不同编译器处理不一样,4*4=16,这里x先两次自增到4,再乘。有的编译器不一样,故宏避免使用增量减量
        system("pause");
        return 0;
    }

    宏里的#和##运算符

    #include <stdio.h>
    #include <stdlib.h>
    #define PSQR(X) printf("x的平方 = %d
    ", ((X) * (X)));//注意这里的;是printf语句的分号
    int main(void)
    {
        PSQR(3);
        system("pause");
        return 0;
    }

    结果是 x 的平方 = 9

    注意,printf引号里的x被看作是普通的文本,而不是一个可以被替换的语言符号!可以在类函数宏的替换部分,使用#运算符做预处理,这样普通文本转换为可以被替换的语言符号!如果x是宏参数,那么#x可以把参数名转换为相应的字符串处理,就可以输出3,而不是X!(该过程也叫字符串化)。

    #define PSQR(X) printf(#X "的平方 = %d
    ", ((X) * (X)));
    int main(void)
    {
        PSQR(3);
        system("pause");
        return 0;
    }

    3的平方 = 9

    调用宏,用 ”3” 代替 #X ,标准C把这些字符串链接起来,产生最终结果。

    #只可以用于类函数宏,而##运算符既可以用于类函数宏,也可以用到类对象宏。

    ##可以把两个语言符号组合为单个语言符号!

    #define XNAME(n) X##n //类函数宏里,n是宏参数(一个语言符号),X是一个语言符号,加上##,两者变一个语言符号
    #define PRINT_XN(n) printf("x" #n " = %d 
    ", X ## n);//注意这里X必须大写,因为XNAME里X大写了
    int main(void)
    {
        int XNAME(1) = 14;//相当于 int x1 = 14;
        PRINT_XN(1);
        system("pause");
        return 0;
    }

    打印 x1 = 14

    可变宏

    函数可以接收固定参数,也可以接收可变参数(printf函数)。同样宏也是这样。

    注意:可变,字符串化是c的词汇,但是固定函数,固定宏,不变宏等不是C的词汇。

    实现思想:宏定义的参数列表最后一个参数为省略号…(三个点),预定义宏__VA_ARGS__就可以用在被替换部分,说明省略号的含义。

    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #define PR(X, ...) printf("可变宏:" #X  __VA_ARGS__ );//注意这里是两个下划线__
    int main(void)
    {
        double x = 48;
        double y;
        y = sqrt(x);
        //第一个参量对应宏的X,值为1,则#X 变为1,宏展开后成为:
        //printf("可变宏:" "1" "x = %g
    ", x);
        //然后三个字符串被编译器连接为:
        //printf("可变宏:1x = %g
    ", x);
        //最后指向输出
        PR(1, "x = %g
    ", x);//__VA_ARGS__告诉了省略号代表了什么
        //注意:省略号只能代表最后一个参数!!!
        system("pause");
        return 0;
    }

    宏是用空间换取时间,函数是用时间换取空间,比如,使用宏100次,那么会在程序里插入100次宏代表的代码,浪费空间,但是速度很快。使用函数100次,程序只有一份函数的拷贝,节省空间,但是每次调用函数,都要压栈出栈,返回调用点,浪费时间!

    简单函数就能处理的功能,一般用宏,或者内联函数。

    宏名字不能有空格,必须大写(约定俗成的,提醒程序员这是宏),宏不检测类型只是简单的文本替换,好处是int类型,或者double类型等都能使用。

    用圆括号括起来类函数宏里的每个参数,好习惯,避免文本替换造成逻辑混乱的错误。

    欢迎关注

    dashuai的博客是终身学习践行者,大厂程序员,且专注于工作经验、学习笔记的分享和日常吐槽,包括但不限于互联网行业,附带分享一些PDF电子书,资料,帮忙内推,欢迎拍砖!

  • 相关阅读:
    如何制作Python百分比进度条
    如何按列表的元素中的第二个元素排序
    map的用法Python
    上一个问题增加用户名密码登陆
    最近alex买了个Tesla Model S,通过转账的形式,并且支付了5%的手续费,tesla价格为95万。账户文件为json,请用程序实现该提现行为。
    最近alex买了个Tesla Model S,通过转账的形式,并且支付了5%的手续费,tesla价格为95万。账户文件为json,请用程序实现该转账行为。
    写一个6位随机验证码程序,要求验证码中至少包含一个数字,一个小写字母,一个大写字母
    传统html和html5网页布局
    图片路径问题
    面向对象编程的一个形象比喻
  • 原文地址:https://www.cnblogs.com/kubixuesheng/p/4314881.html
Copyright © 2020-2023  润新知