zoukankan      html  css  js  c++  java
  • C语言-宏的使用

    预处理指令的特点

    • 1,以#开头
    • 2,预处理指令都是在编译之前执行的
    • 预处理指令后面都是没有分号

    C 程序从编写到编译,链接,执行的一个流程

    • 1,新建一个.c 源文件,.c 的文件是c 程序的源文件
    • 2,在.c 的源文件中写上符合 c 语法规范的源代码
      • C语言严格区分大小写
      • 除了字符串常量,其他的地方必须使用英文输入法
    • 3,使用 cc -c 指令去编译源文件
      • 格式:cc -c 源文件名称
      • 编译做的事情
      • a,先执行源文件中的预处理指令,如果有文件包含指令,就将文件的内容拷贝到指令的地方
      • b,检查 .c 文件中的语法是否符合规范
        • 1,如果符合,生成.o 目标文件,就是.c 文件对应的二进制指令
        • 2,如果不符合语法规范,就报错,不会生成.o 的目标文件
    • 4,链接
      • cc xx.o
      • a,为.o 的目标文件添加启动代码
      • b,链接函数,告诉编译器要调用的函数在什么地方,调用的时候,去正确的地方找实现
      • c,链接成功以后,就会生出一个可执行文件,这个文件就是我们程序了

    预处理指令的分类

    • 文件包含指令 # include
    • 宏定义:可以将一段 c 代码定义为 1 个标识,使用这个标识就可以使用这段代码
    • 条件编译指令:

    宏定义:

    • 他是一个预处理指令,所以他在编译之前执行
    • 作用:
      • 可以为一段 C 代码定义一个标识,如果你要使用这个 c 代码,那么就是用这个标识就可以了
    • 语法:
      • ----#define 宏名 宏值
      • ----#define N 10
    • 如何使用宏
      • 在C 代码中,直接使用宏的名称就可以了
    • 宏的原理是
      • 在预编译的时候,就会执行源文件中的预处理指令,会将 C 代码中使用宏名的地方替换为宏值,
      • 将 C 代码中的宏名替换为宏值的过程就叫做宏替换

    在使用宏的时候需要注意的地方

    • 宏值可以是任意的东东(常量,表达式),在定义宏的时候并不会去检查语法,但是替换的时候就会检查语法了
    #include <stdio.h>
    # define M 10
    # define N 10+10+10
    int main(int argc, const char * argv[]) {
        int num = M+10;
        printf("M = %i
    ",M);
        printf("num = %i
    ",num);
        
        // 宏值是整个表达式,而并不是计算的结果
        int num1 = N * 2;
        printf("num1 = %i
    ",num1);
        return 0;
    }
    
    
    • 无论宏值是什么东西,在定义宏的时候不会去检查语法,只有当完成了宏替换的时候才会去检查替换以后是否符合语法规范

    • 如果宏值是一个表达式,宏值并不是这个表达式的结果,而是这个表达式的本身

    • 如果宏值中包括一个变量名,那么在使用这个宏之前必须保证这个变量存在不然就会报错

    # define X a+a
    int main(int argc, const char * argv[]) {
        int a = 10;
        int num2 = X;
        
        return 0;
    }
    
    • 无法通过赋值符号给宏改值,因为宏根本就不是变量
    • 宏的作用域问题
      • a, 宏可以定义在函数里面也可以定义在函数外面
      • b,从定义宏的地方,后面的所有的地方都可以使用这个宏,跟变量不一样,就算这个宏定义在这个大括号里面,在这个后面也是可以访问的
      • undef 宏名 -->解除宏指令
      • 默认情况下,宏从定义的地方一致到文件结束都可以使用,#undef 宏名可以让指定的宏提前消失
    • 字符串中如果出现了宏名,并不会认为是一个宏,而是认为是字符串的一部分,字符串中并不会出现宏替换
    • 宏的层层替换
      • 宏值当中我们用到了另外一个宏名,那么就会将这个宏值当中的宏名替换为对应的宏值
    # define P 3
    # define A 5
    # define JISUAN P*A
    
    int main(){
        printf("JISUAN = %i
    ",JISUAN);
    }
    
    • 如果后面跟了分号,那么就会将分号作为宏值的一部分,但是建议不要这么写
    • C语言的代码都可以用作宏值
    # define p printf
    # define d "%i"
    int main(){
        p(d,10);
        return 0;
    }
    

    define 和 typedef 的区别

    • ----#define 是一个预处理指令,在预编译的时候执行,在预编译的时候会把宏名替换为宏值
      • typedef 是一个 C 代码.在运行的时候执行
    • ----#define可以将任意的C 代码取 1 个标识名
      • typedef 只能为数据类型取名字

    宏的高级用法

    • 我们在定义宏的时候,宏名是可以带参数的,在这个宏值当中,可以直接使用这个参数
    • 如果使用的宏有参数,那么就必须要在使用它的时候为这个宏的参数传值
    # define B(a) a+10+a
    
    int main(){
        // 有参数的宏
        int num3 = B(8);
        printf("num3 = %i
    ",num3);
        return 0;
    }
    

    image-20200615225818231

    • 宏替换的原理是什么?
      • 先将传入的值传递给宏的参数,那么宏的参数的值就是我们传递的值
      • 再把宏值当中使用参数的地方换成参数的值
      • 最后,再将使用宏名的地方,替换为最后的宏值
    # define MAX(a,b) a > b ? a:b
    int main(){
        printf("最大值是:%i
    ",MAX(10, 0));
        return 0;
    }
    

    使用带参数的宏注意点

    • 宏不是函数,所以宏的参数不需要写数据类型,直接写参数名称即可
    • 我们在定义宏的时候,编译器是如何区分宏名和宏值的呢?
      • ----#define空格宏名空格宏值 --->两个空格之间的就是宏名
    • 为带参数的宏传递的时候是本色传递(本色传递:你给的什么,那就是什么,预编译的时候啥都没有就是直接本色的替换)
    • 宏一定程度上可以实现和函数一样的效果
      • 宏一旦换了行就代表宏定义结束了,
      • 代码很少的时候会使用宏,只有一句,两句的时候可以使用宏

    条件编译指令

    • 他也是一个预处理指令,所以在预编译阶段执行
    • 作用:默认情况下我们所有的 C 代码都会被编译为二进制代码,条件编译指令的作用:可以让编译器只编译置顶的部分
    • 条件编译指令的一种用法
      • if 条件
      • c 代码
      • endif
    # define M 10
    int main(){
            // 条件编译指令
    #if M == 10
        printf("我是条件编译指令的代码
    ");
    #endif
        return 0;
    }
    
    // 输出:我是条件编译指令的代码
    
    • 注意:条件只能是宏,不能是变量,因为:预编译的时候变量还没有呢
        int m = 10;
    #if m == 10
        printf("我是 变量 m == 10 才会执行的代码
    ");
    #endif
    
    • 条件编译指令的第二种用法
    # define M 10
    
    int main(){
        #if M > 0
        printf("M>0编译这个代码
    ");
    #elif M > 0 && M < 20
        printf("M > 0 && M < 20编译这个代码
    ");
    #elif M >20 && M < 40
        printf("M >20 && M < 40编译这个代码
    ");
    #else
        printf("否则编译这个代码");
    #endif
        
        return 0;
    }
    
    • 条件编译指令和 if 语句的对比
      • 1,条件编译指令是一个预处理指令,在预处理阶段执行,if 语句是 C代码.在程序运行的时候执行
      • 2,if 语句全部都要被编译为二进制指令,条件编译指令只会编译条件符合的代码
      • 3,实际上,if 语句一定程度上可以换成条件编译指令,但是条件编译指令的条件不能是变量参与,只能是宏
    • 条件编译指令的第三种用法,如果定义了指定的宏,就编译指定的代码
    #define M 10
    int main(){
            // 第三种用法
    #if M
        printf("如果定义了这个宏,就执行这个代码
    ");
    #endif
        return 0;
    }
    
    int main(){
            // 第三种用法
    #ifndef M
        printf("如果没有定义了这个宏,就执行这个代码
    ");
    #endif
        return 0;
    }
    

    条件编译指令的应用场景

    // 在调试的时候,调试信息有很多,又不想每次都一个一个的删除,那么可以使用条件编译指令,每次只需要修改自己定义的宏就可以对 printf 函数的控制
    
    #include <stdio.h>
    
    
    # define SHAN_DEBUG 0
    # if SHAN_DEBUG == 0
        # define print(val1,val2) printf(val1,val2)
    # else
        # define print(val1,val2)
    # endif
    
    
    int main(int argc, const char * argv[]) {
    
        print("%d",10);
        
        
        
        return 0;
    }
    
    
    
    // 无论一个文件包被#include 多少次,我只引入一次
    #ifndef lianxi_h
    #define lianxi_h
    
    #include <stdio.h>
     // 写自己的代码
    #endif /* lianxi_h */
    
    // 原理:在 main.c中#include lianxi.h,导入很多次,#include 的作用就是将此文件全部复制过来,所以复制第一次在编译的时候如果没有导入 lianxi.h 这个文件,全部被copy 过来,此时没有定义 lianxi.h 这个宏,那么就定义这个宏 lianxi.h,然后在第二次copy 过来的时候,发现定义了这个 lianxi.h 这个宏,所以就不会重复的 copy 了
    
  • 相关阅读:
    linux 文件系统管理三部曲之二:创建文件系统
    linux 文件系统管理三部曲之一:磁盘分区
    Django 链接MySQL及数据操作
    redis事务
    redis配置文件详解
    redis中hash数据类型
    redis的基础知识
    redis两种持久化方式RDB和AOF
    git命令
    .gitignore中添加的某个忽略文件并不生效
  • 原文地址:https://www.cnblogs.com/shanshan-test/p/13157147.html
Copyright © 2011-2022 走看看