C语言DAY 14 - 预处理指令

来源:互联网 发布:单片机程序设计实例 编辑:程序博客网 时间:2024/06/12 04:59

        • 预处理指令的概述
    • 3 条件编译指令 只编译指定的C代码为二进制指令
        • 宏定义
          • 宏定义
          • 在使用宏的时候需要注意的地方
          • define 和 typedef 的区别
    • typedef只能为数据类型取名字
        • 带参数的宏
  • define 空格 宏名 空格
    • 3 代码多的情况下还是建议使用函数
        • 条件编译指令
        • 条件编译指令的应用场景
            • 在调试程序的时候我们会写一些代码调试完成还要去一个个删除很麻烦
            • 同一个头文件被包含多次
    • endif
        • static 与 extern
        • 全局变量
        • main 函数的参数和返回值

预处理指令的概述

  1. 预处理指令的特点

    1. 都是以#开头.
    2. 预处理指令都是在编译之前执行.
    3. 预处理指令后面都没有分号.
    1. C程序从编写到编译、链接、执行的一个流程.
  2. 新创建1个.c的源文件. .c的文件是C程序的源文件.

  3. 在.c的源文件中写上符合C语法规范的源代码.

    a. C语言严格区分大小写.
    b. 除了字符串常量, 其他的地方必须使用英文输入法.

  4. 使用cc -c 指令去编译源文件

    格式: cc - c 源文件名称编译做的事情.a. 先执行源文件中的预处理指令.   如果有文件包含指令,就将文件的内容拷贝到写指令的地方.   ....b. 检查.c文件中的语法是否符合规范.   b.1 如果符合: 生成.o目标文件. 就是.c文件对应的二进制指令.   b.2 如果不符合语法规范. 就报错. 不会生成.o的目标文件.
  5. 链接
    cc xx.o

     a. 为.o的目标文件添加启动代码. b. 链接函数. 告诉编译器,要调用的函数在什么地方.    调用的时候,去正确的地方找实现. c. 链接成功以后. 就会生成1个可执行文件.    这个文件就是我们程序了.
    1. 预处理指令的分类.

    1). 文件包含指令 #include

    2). 宏定义: 可以将1段C代码定义为1个标识,使用这个标识就可以使用这段代码.

3). 条件编译指令: 只编译指定的C代码为二进制指令.

宏定义

宏定义.
  1. 它是1个预处理指令.所以它在编译之前执行.

  2. 作用: 可以为1段C代码定义1个标识.如果你要使用这d段C代码.那么你就使用这个标识就可以了.

  3. 语法:

    #define 宏名 宏值#define N 10
  4. 如何使用宏.

    在C代码中.直接使用宏的名称就可以了. int a = N + 1;
  5. 宏的原理

    在预编译的时候,就会执行源文件中的预处理指令.会将C代码中使用宏名的地方替换为宏值.将C代码中的宏名替换为宏值的过程叫做  宏替换/宏代换.
在使用宏的时候需要注意的地方.
  1. 宏值可以是任意的东东. 宏值可以是任意的东西. 在定义宏的时候,并不会去检查语法.
  2. 无论宏值是什么东西,在定义宏的时候,不会去检查语法
    只有当完成了宏替换的时候,才会去检查替换以后,是否符合语法规范.
  3. 如果宏值是1个表达式,那么宏值并不是这个表达式的结果,而是这个表达式本身.
  4. 如果宏值中包括1个变量名,那么在使用这个宏之前必须要保证这个变量已经存在.
  5. 无法通过赋值符号为宏 改值. 因为宏根本就不是变量.
  6. 宏的作用域.

    a. 宏可以定义在函数的内部 也可以定义在函数的外部.b. 从定义宏的地方,后面的所有的地方都可以直接使用这个宏.   就算这个宏定义在这个大括弧里面, 在这个后面 哪怕是大括弧外面都可以访问.c. 默认情况下,宏从定义的地方一直到文件结束都可以使用.   #undef 宏名   可以让指定的红提前失效.
  7. 字符串中如果出现了宏名,系统不会认为这是1个宏,而认为是字符串的一部分.
    字符串中并不会出现宏替换.
  8. 宏的层层替换.

     宏值当中我们用到了另外1个宏名. 那么就会先将这个宏值当中的宏名替换为对应的宏值.
  9. 如果后面跟了分号.那么就会吧分号作为宏值的一部分
    替换的时候会连分号一起.
    虽然可以,但是不建议你加分号.
define 和 typedef 的区别
1).  #define是1个预处理指令.在预编译的时候执行. 在预编译的时候会吧宏名换成宏值.     typedef 是1个C代码. 在运行的时候才会执行.2).  #define 可以将任意的C代码取1个标识名.

typedef只能为数据类型取名字.

带参数的宏

  1. 我们在定义宏的时候,宏名是可以带参数的.
    在这个宏值当中.可以直接使用这个参数

    1. 如果使用的宏有参数,那么就必须要在使用它的时候为这个宏的参数传值.

    2. 宏代换的原理是什么?

      1). 先将传入的值传递给宏的参数,那么宏的参数的值就是我们传递的值.

      2). 再把宏值当中使用参数的地方换成参数的值.

      3). 最后,再将使用宏名的地方,替换为最后的宏值.

    3. 使用带参数几点注意

      1). 宏不是函数,所以宏的参数不需要加类型说明符. 直接写参数名就可以了.

      2). 我们在定义宏的时候,编译器是如何区分宏名和宏值.

      define 空格 宏名 空格.

      第1个空格和第2个空格之间的内容会作为宏名
      第2个空格以后的内容会作为宏值.

      所以.在写宏的时候,空格要小心了.

      3). 为带参数的宏传值的时候,是本色传递.

      如果传递1个变量,并不是传递这个变的值
      而是直接传递的就是这个变量的串.

    4. 宏一定程度上可以实现和函数一样的效果.

      1). 宏值一旦换行,就任何宏定义结束了. 所以你打算使用宏去替换函数的话,那么些的很恶心.

      2). 只有当函数的代码很少的时候,只有几句的时候,就可以使用宏.

3). 代码多的情况下,还是建议使用函数.

条件编译指令

  1. 条件编译指令.

    1). 它是1个预处理指令. 所以在预编译阶段执行.2). 作用:默认的情况下,我们所有的C代码都会被编译为二进制代码.条件编译指令的作用: 可以让编译器只编译指定部分的代码.
  2. 条件编译指令的第一种用法.

    #if 条件    C代码;#endif

    在预编译的时候,如果条件成立. 就会将其中的C代码编译成二进制指令.
    如果条件不成立.就不会将其中的C代码编译成二进制指令.

    注意: 条件只能是宏 不能是变量.

  3. 条件编译指令的第二种用法

    #if 条件    C代码;#elif 条件    C代码#elif 条件    C代码#else    C代码#endif
  4. 条件编译指令和if语句的1个对比

    1).条件编译指令是1个预处理指令.在预处理阶段执行.
    if语句是C代码. 在程序运行的时候执行.

    2). if语句无论如何全部都要被编译为二进制指令.
    条件编译指令: 只会讲符合条件的C代码编译为二进制指令.

    3). 实际上.if语句一定程度上可以换成条件编译指令.
    但是.条件编译指令的条件不能是变量参与.只能是宏.

  5. 条件编译指令的第三种用法.

    1. 如果定义了指定的宏.就编译其中的代码否则就算了.
    #ifdef 宏名        C代码;    #endif
2. 如果没有定义指定的宏.就编译其中的代码否则就算了
    #ifndef 宏名    #endif

条件编译指令的应用场景

在调试程序的时候我们会写一些代码,调试完成还要去一个个删除很麻烦
//定一个标记宏.0是执行下面代码,其他的值就不执行#define test 0#if test == 0#define LOG(val1,val2) printf(val1,val2)#else//宏接收值,但是宏替换内容是空的.所以调试完成就可以全部替换为空的代码#define LOG(val1,val2)#endif
同一个头文件被包含多次
//如果没定义 BAOHAN 这个宏就执行下面的代码#ifndef BAOHAN//没定义就先定义一次#define BAOHAN//要执行的代码#include <stdio.h>//结束 if 判断

#endif

static 与 extern

  1. static、extern 是用来修饰变量和函数的.
  2. 修饰局部变量

1). 被static修饰的变量 我们叫做静态变量.
静态变量是声明在常量区中的.

    静态变量在程序一启动的时候,就已经声明在常量区中.    当我们执行函数的时候.不会再去执行声明静态变量的那句话.直接就略过.    并且函数执行完毕之后,静态变量也不会被回收. 直到程序结束的时候才会被回收.    无论函数执行多少次.静态变量只会有1个.

2). 与全局变量的区别.

    全局变量可以在所有的函数里面访问.而静态变量只能在定义静态变量的函数中访问.

3). 什么时候需要将变量定义为静态的呢?

    a. 特点和作用.       无论函数执行多少次.这个静态变量都只有1份. N次函数共享.    b. 当你有1个数据.你希望无论函数执行多少次 这个数据只有1份.    实际上,如果要实现这个效果,也可以直接将这个数据定义成全局变量.    但是全局变量是被所有的函数共享. 所有的函数都能访问    而静态变量的特点. 只能在当前函数中访问 并且多次执行只有1个.

4). extern不能修饰局部变量.

全局变量

  1. 创建1个函数的完整步骤.

    a. 写函数的声明b. 在写函数的实现.
  2. 创建1个全局变量的完整步骤.

    a. 先写全局变量的声明.
    只声明1个全局变量,不去初始化.这个叫做全局变量的声明.

    int num;

    b. 写全局变量的实现.
    声明这个全局变量.为其赋值.
    int num = 10;

    这个时候,全局变量的声明可以放在使用全局变量的前面,全局变量的实现可以放在使用全局变量的后面.
    注意:

    a. 全局变量可以只有声明没有实现. 这个时候这个全局变量就会默认实现为0.
    b. 全局变量也可以只有实现没有声明. 但是这个时候这个全局变量的实现必须要放在使用全局变量的前面.

    1. 如果在模块中要声明全局变量.

    1). 这个全局变量就要分为声明和实现.
    声明写在.h 文件中.
    实现写在.c 文件中.

    2). 全局变量分为两种.

    a. 内部全局变量.   使用static修饰的全局变量.叫做内部全局变量.   这个全局变量就只能在当前模块中访问.   如果是跨模块访问就无法正确访问.b. 外部全局变量.    使用extern修饰的全局变量.我们叫做外部全局变量.    这个全局变量就可以在外部的模块中访问.

4.关于全局变量.
1. 全局变量

1). 如果要在模块中声明全局变量的    .h 写全局变量的声明.    .c 写全局变量的实现.2). 声明在模块中全局变量.必须要使用extern 或者 static 修饰.    否则 包含编译 就会报错.3). 模块中的全局变量如果使用extern修饰,那么这个模块中全局变量就可以在其他模块中访问.    模块中的全局变量如果使用static修饰,那么这个模块中全局变量就不能在别的模块中正常访问.4). 如果头文件中全局变量的声明我们使用的是static.那么.c实现文件中也必须要使用static    如果头文件中全局变量的声明我们使用的是extern 那么.c实现文件中就可以不使用extern了.

2. 修饰函数.

1). 被static修饰的函数,只能在这个模块的内部调用,无法跨模块调用.2). 被extern修饰的函数,可以跨模块调用.3). 函数如果没有写static或者extern那么默认就是extern 外部的.

main 函数的参数和返回值

  1. main函数的参数,可以写也可以不写.不会有太大的影响.

  2. 根据最新的C标准. C语言的main函数要返回1个int类型的数据.

    main函数是系统在调用.

    系统日志: 可以查看系统中某个程序的运行时间,结束的时间.如何结束的.

    main函数的返回值代表这个程序是如何结束的.

    0 代表正常结束.
    非0代表非正常结束. 这个时候系统就会额外的多做1些事情.

    main函数如果不写返回值.默认就是返回0.

    1. main函数的参数
  1. int argc 代表后面那个字符串数组的长度.

  2. const char * argv[]. 是1个数组.
    这个数组的第0个元素代表: 代表这个程序的路径.

  3. 将数据传递到函数内部使用的方式.

    a. scanf函数.b. 就是执行程序的时候.在终端执行程序的时候,可以将要传入的数据以参数的形式写在程序名后面.   ./a.out xxx yyy jjj   这个时候,通过参数argv数组就可以拿到通过这种方式传入的数据.
  4. main函数的参数. 特别是那个字符串数组.

    第0个元素的值 是当前程序的路径.允许运行程序的时候.由用户在终端传入数据.终端传入的数据可以通过数组拿到.

  1. 我们在Xcode中写的程序.按下运行按钮.
    自动的编译 链接 成1个可执行文件.

  2. 我们可以找到这个可执行文件,将他单独的在终端运行.

  3. 在终端中运行程序的时候.可以给这个程序传递一些数据.

    程序名称 数据1 数据2 数据3…..

  4. 在程序内部 如何拿到从终端中拿过来的数据?

    就是通过参数数组 argv 拿到.

    第0个元素代表当前这个程序的路径.
    从第1开始.每1个元素的值就是 在终端中传入的数据.

  5. 如果你希望某个变量的值不是在写代码的时候写死.
    而是程序运行的时候,由用户输入.

    1). scanf

    2). 使用终端传数据.


0 0