0722预处理

复习:

1、输出缓冲区

    程序输出的数据并没有立即写入"文件",而是先存储到输出缓冲区中,当满足一定条件时才写入文件中:

    1、遇到'\n'

    2、遇到输入语句

    3、缓冲区满4k

    4、程序结束

    5、手动刷行 fflush(stdout)  windows没有

2、输入缓冲区

    在终端输入的数据,按下回车后,会从终端存储进输入缓冲区中,再执行输入函数时,会从输入缓冲区中读取相应的数据

    1、当想要从缓冲区中读取整型、浮点型数据,而缓冲区中的数据是字符、字母时,此次读取会失败,而且不会从缓冲区中拿走该数据,导致接下来所有数据的读取都出问题

        解决方法:根据scanf的返回值判断是否全部成功接收数据,否则就清空输入缓冲区再重新输入

            stdin->_IO_read_ptr = stdin->_IO_read_end;

    2、使用fgets输入时,超过size-1个字符就会残留在输入缓冲区中,影响下一次的输入

        解决方法:判断缓冲区中是否会有'\n',如果有

            scanf("%*[^\n]");

            scanf("%*c");

            或者

            stdin->_IO_read_ptr = stdin->_IO_read_end;

    3、先输入其他类型数据,再输入字符、字符串数据,那么其他类型数据输入时最后按下的'\n'或者空格就会被字符、字符串接收,影响正常的输入

        字符:

        scanf(" %c");

        字符串:

        scanf("%d",&num);

        scanf("%*c");

        gets(str);

3、常考的字符串处理函数

    stelen\strcpy\strcat\strcmp

    strlen和sizeof区别

    memcpy\memcat\memcmp\memmove

    sprintf 把各种类型数据拼接成字符串

    sscanf  从字符串解析各种类型数据到变量中

一、预处理指令

    程序员所编写的代码并不是真正的标准c代码,需要一段程序翻译成标准c代码,才能被编译器编译

    翻译的过程叫做预处理,负责翻译的程序叫做预处理器,被翻译的语句叫做预处理指令,以'#'开头的都是预处理指令

    gcc -E code.c       直接显示预处理后的结果

    gcc -E code.c -o code.i 生成预处理文件

二、预处理指令的分类

    '#include'    导入头文件/头文件包含

    '#include <>' 从系统指定路径查找并导入头文件

    '#include ""' 先从当前工作路径查找,找不到,再从系统指定路径查找并导入头文件

    gcc code.c -I path   编译时指定头文件的加载路径为path,最先从该路径查找

        可以通过设置环境变量来修改、增加系统指定头文件加载路径  

        修改~/.bashrc 终端配置文件

    '#define'   定义宏

        宏常量:'#define' 宏名 常量值

                '#define MAX 100'

            其实本质上就是,在代码中出现了宏名的地方,在预处理时替换为对应的常量值

            优点:提高可拓展性、可读性、安全性,还可以用在case后面

            注意:一般宏名全部大写,末尾不加分号

                局部变量、函数名全部小写,全局变量首字母大写,数组arr,指针p、pp,字符串str

        预定义好的宏:

            __func__    获取函数名

            __FILE__    获取文件名

            __DATE__    获取当前日期(月、日、年)

            __TIME__    获取当前时间

            __LINT__    获取当前行数 %d

        宏函数:其实就是带参数的宏

            '#define' 宏名(参数名) 替换的代码

            宏函数不是真正的函数,不检查参数类型、没有传参、只有表达式的计算结果没有返回值

            '#define SUM(a,b) a+b'

            SUM(num1,num2)

            替换过程:

                1、先把代码中使用到宏函数的地方替换为宏函数后面的代码  a+b

                2、再把宏函数代码中使用到的参数替换为调用者提供的数据 num1+num2

        注意:宏函数后面的替换代码不能直接换行,需要在每一行末尾通过续航符 \ 换行

            如果有多行代码,可以使用大括号保护代码

        宏的二义性:

            由于宏代码所处位置、参数位置、优先级问题,导致同一个宏函数有不同的可解释结果

        如何避免宏的二义性:

            1、给每个参数加小括号,给整个式子加小括号

            2、在使用宏函数时,不要提供自变运算符的参数

            注意:容易出选择题

    练习1:实现一个交换两个变量的值的宏函数,数据类型通用,能写多少个,分析优劣

        '#define SWAP(a,b) {typeof(a) (t)=(a);(a)=(b);(b)=(t);}'

        /day08/6.c

    常考笔试面试题:

        '#define'与'typedef'的区别

        '#define INT int'

        'typedef int INT'

        INT num

        如果是普通类型,它们功能上没有任何区别

        '#define INTP int*'

        'typedef int* INTPP'

        INTP p1,p2,p3   //p1是int*  p2,p3是int

        int* p1,p2,p3   //p1,p2,p3都是int*

    宏函数和普通函数的区别?

        它们是什么?

        宏函数:带参数的宏替换,只是代码替换,只是使用时像函数而已,不是正在的函数

        函数:是一段具有某项功能的代码集合,会被翻译成二进制指令存储代码段,函数名就是它的首地址,有独立的栈内存、命名空间

        有什么不一样:

        宏函数:运算结果    通用    危险        替换         速度快    代码冗余

        函数:  返回值    类型检查  安全    内存申请、释放    速度慢    函数跳转

        注意:调用频繁、内容简单的功能适合写成宏函数

    条件编译:

        根据条件决定哪些代码是否参与最终的编译

    版本控制:

        '#if 条件'

        '#elif 条件'

        '#else'

        '#endif'

    头文件卫士

        防止头文件被 重复 包含

        '#ifndef'   宏名

        'define'    宏名

        'endif'     //宏名

        宏名:头文件名全部大写,小数点用下划线代替

        注意:头文件中必加上头文件卫士

   

    调试、判断:

        '#ifdef 宏名'

        'else'

        'endif'

        gcc code.c -D宏名

        可以在编译时定义宏

    '#ifdef DEBUG

        #define debug(...) printf(__VA_ARGS__)

    #else

        #define debug(...)

    #endif'

    打印错误信息宏函数

    ' #define error(...) printf("%s:%s %d %s:%m %s %s\n",__FILE__,__func__,__LINE__,__VA_ARGS__,__DATE__,__TIME__)'

三、头文件

    头文件中应该写什么?

    问题:头文件可能会被任意源文件包含,意味着头文件中的内容会在多个目标文件中同时存在,因此要满足合并内容不能有冲突

    重点:头文件中只能编写声明语句,绝对不能有定义语句

        函数声明

        全局变量声明

        宏常量

        宏函数

        typedef 类型重定义

        结构、联合、枚举的类型说明

    头文件的编写原则

        1、为每个.c文件编写一份.h文件,.h文件是对.c文件的说明

        2、如果需要用到某个.c文件中的全局变量、函数、宏等内容时,只需要把它的头文件导入即可

        3、.c文件也要导入它自己的.h文件,目的是为声明与定义一致

    头文件的相互包含:

        假设a.h包含了b.h,b.h又包含了a.h,此时编译会出错

        解决方案:把a.h需要导入b.h的内容和b.h需要导入a.h的内容提取出来到另一个头文件c.h、

        错误提示:未知类型名"XXXX",一般就是头文件相互包含

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值