初学者的笔记-----标准C语言day13

复习:

1、输出缓冲区

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

                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、常考的字符串处理函数

        strlen\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__     获取函数名       %s

                        __FILE__      获取文件名      %s

                        __DATE__    获取当前日期    %s

                        __TIME__    获取当前时间    %s

                        __LINE__     获取当前行数   %d

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

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

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

                        #define SUM(a,b) a+b

                        替换过程:

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

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

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

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

        宏的二义性:

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

        如何避免宏的二义性:

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

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

                注意:容易出选择题

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

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

        常考笔试面试题:

                #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

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

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

                它们是什么?

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

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

                有什么不一样?

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

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

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

        条件编译:

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

        版本控制:

                #if     条件

                #elif   条件

                #else

                #endif

        ★头文件卫士:

                防止头文件被 重复 包含

                #ifndef 宏名

                #define 宏名

                #endif//宏名

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

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

        调试、判断:

                #ifdef 宏名

                        如果宏名定义了,则此处代码参与编译

                #else

                        否则,此处参与编译

                #endif

                gcc code.c -D宏名

                可以在编译时定义宏

                定义打印调试信息宏函数,是否打印提示信息,取决于事都定义了DUBUG宏

                #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

                错误提示:unknown type未知类型名"XXXX",大概率就是头文件相互包含

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值