复习: 1、输出缓冲区 ? ? 程序输出的数据并没有立即写入到"文件",而是先存储到了缓冲区中,当满足一定条件时才会写入到文件中 ? ? 1、遇到\n ? ? 2、遇到输入语句 ? ? 3、缓冲区满4k ? ? 4、程序结束 ? ? 5、手动刷新fflush 2、输入缓冲区 ? ? 在终端输入的数据此时由终端保管,当按下回车后,数据由终端写入到程序的输入缓冲区,scanf函数再从缓冲区中读取数据到给变量赋值 ? ? 1、当想要输入的是整形、浮点型数据,而缓冲区中的数据是字符或符号时,此时类型不匹配读取失败,并且不会从缓冲区中拿走符号或字符, ? ? 导致接下来的数据读取都失败 ? ? ? ? 解决方法: ? ? ? ? ? ? 根据scanf的返回值判断变量是否从缓冲区中读取成功,如果失败,则先清空缓冲区再重新scanf,直到成功为止 ? ? ? ? ? ? ? ? scanf("%*[^\n]") ? ? ? ? ? ? ? ? scanf("%*c");
? ? ? ? ? ? ? ? stdin->_IO_read_ptr = stdin->_IO_read_end;(只限Linux) ? ? 2、当使用fgets读取字符时,如果输入的字符超过size-1个,则缓冲区中会残留超出部分字符,这样会影响接下来的数据读取 ? ? ? ? 解决方法: ? ? ? ? ? ? 判断字符串的最后一个字符是否是\n,如果不是则说明缓冲区中有残留数据,想要清理缓冲区? ? ?? ? ? 3、当输入其他数据类型,紧接着输入字符、字符串型数据时,前一次可能会残留一个\n,会影响后面的字符、字符串的输入 ? ? ? ? 输入字符: ? ? ? ? scanf(" %c"); ? ? ? ? 输入字符串: ? ? ? ? gets(str1); ? ? ? ? scanf("%*c"); ? ? ? ? gets(str);
3、常考的字符串处理函数 ? ? strlen(\0 不算)、strcpy、strcmp、strcat ? ? memcpy、memset、memcmp
4、常用的字符串处理函数 ? ? sscanf ? ? ?解析字符串 ? ? sprintf ? ? 把各种类型的数据和提示信息拼接成字符串
预处理指令: ? ? 程序员所编写的代码并不能被真正的编译器编译,需要一段程序把代码翻译一下 ? ? 翻译的过程叫预处理,负责翻译的程序叫做预处理器,被翻译的代码叫做预处理指令,以#开头的代码都是预处理指令
? ? 查看预处理过程 ? ? ? ? gcc -E code.c ? 把预处理的结果显示到终端上 ? ? ? ? gcc -E code.c -o code.i 把预处理结果存储到code.i预处理文件中
? ? 预处理指令的分类: ? ? ? ? #include 文件包含 ? ? ? ? ? ? #include <> 从系统指定路径查找并导入头文件 ? ? ? ? ? ? #include "" 从当前路径查找,如果找不到再从系统指定路径查找并导入头文件 ? ? ? ? ? ? 通过编译参数指定查找路径 -I /path ? ? ? ? ? ? 操作系统通过设置环境变量来指定头文件的查找路径 ? ? ? ?? ? ? ? ? #define 定义宏 ? ? ? ? ? ? 宏常量: ? ?#define 宏名 数据 ? ? ? ? ? ? ? ? #define MAX 50 ? ? ? ? ? ? ? ? 优点:提高代码可读性、提高可扩展性(方便批量修改)、提高安全性、还可以用在case后面
? ? ? ? ? ? ? ? 注意:一般宏名全部大写,末尾不要加分号
? ? ? ? ? ? ? ? 预定义的宏: ? ? ? ? ? ? ? ? ? ? __func__ ? ?获取函数名 ? ? ? ? ? ? ? ? ? ? __FILE__ ? ?获取文件名 ? ? ? ? ? ? ? ? ? ? __DATE__ ? ?获取当前日期 ? ? ? ? ? ? ? ? ? ? __TIME__ ? ?获取当前时间 ? ? ? ? ? ? ? ? ? ? __LINE__ ? ?获取当前行号
? ? ? ? ? ? 宏函数: ?带参数的宏 ? ? ? ? ? ? ? ? 不是真正的函数,不检查参数的类型,没有传参,只是值替换,没有返回值,只有表达式的计算结果 ? ? ? ? ? ? ? ? #define SUM(a,b,c) ((a)+(b)+(c))? ? ? ? ? ? ? ? ? 1、把代码替换为宏函数后面的表达式代码 ? ? ? ? ? ? ? ? 2、把宏函数代码中使用的参数替换为调用者提供的数据
? ? ? ? ? ? ? ? 注意:定义宏常量、宏函数不能直接换行,可以使用续航符 \ 放在末尾可以换行 ? ? ? ? ? ? ? ? ? ? 也可以使用大括号保护代码
? ? ? ? ? ? 宏函数的二义性 ? ? ? ? ? ? ? ? 由于宏函数所处的位置、参数不同导致宏函数有不同的解释和功能,这种叫做宏的二义性
? ? ? ? ? ? ? ? 如何避免二义性: ? ? ? ? ? ? ? ? ? ? 1、宏函数整体代码加小括号 ? ? ? ? ? ? ? ? ? ? 2、每个参数都加小括号 ? ? ? ? ? ? ? ? ? ? 3、使用宏函数时不要提供带自变运算符的变量作为参数 ? ? ? ? ? ? ? ? 注意:容易出选择题,例如:哪个宏有二义性、选择出宏函数的结果 ? ? 常考的笔试面试题:(C语言中与指针相关的知识点有哪些) ? ? ? ? 如果是普通类型,它们的功能上没有区别 ? ? ? ? #define INI int ? ? ? ? typedef int INT; ? ? ? ? 如果是指针类型 ? ? ? ? #define INIP int* ? ? ? ? INTP p1,p2,p3; //p1是指针,p2p3是int类型变量 ? ? ? ? typedef int* INTP; ? ? ? ? INTP p1,p2,p3; //p1p2p3是指针
? ? ? ? 宏函数与普通函数的区别? ? ? ? ? ? ? 是什么? ? ? ? ? ? ? 宏函数:不是真正的函数,只是代码的替换,用起来像函数 ? ? ? ? ? ? 函数:一段具有某项功能的代码,会被编译成二进制指令存储在代码段中,函数名就是它的首地址,有独立的命名空间、栈空间 ? ? ? ? ? ? 有什么不一样? ? ? ? ? ? ? 函数:返回值 ?类型检查 安全 入栈、出栈 速度慢 跳转 ? ? ? ? ? ? 宏函数:运行结果 通用 危险 替换 速度快 冗余
? ? 条件编译: ? ? ? ? 根据条件决定让代码是否参与最终的编译
? ? ? ? 版本控制: ? ? ? ? ? ? #if ? ? ? ? ? ? #elif ? ? ? ? ? ? #else ? ? ? ? ? ? #endif
? ? ? ? 头文件卫士:防止头文件重复包含 ? ? ? ? ? ? #ifndef ?宏名(头文件名全大写,_替代.) ? ? ? ? ? ? #define ?宏名(头文件名全大写,_替代.) ? ? ? ? ? ? #endif //宏名(头文件名全大写,_替代.) ? ? ? ?? ? ? ? ? 判断、调试代码: ? ? ? ? ? ? #ifdef ?宏名(DEBUG) ? ? ? ? ? ? #else ? ? ? ? ? ? #endif
? ? ? ? ? ? 注意:可以通过编译参数-D宏名 定义宏 ? ? ? ?? ? ? ? ? 封装调试函数 ? ? ? ? #ifdef DEBUG ? ? ? ? ? ? #define debug(...) printf(__VA_ARGS__); ? ? //...是可变长参数 ? ? ? ? #else ? ? ? ? ? ? #define debug(...) ? ? ? ? #endif
? ? ? ? 封装提示错误信息宏函数 ? ? ? ? #define error(...) fprintf(stdout,"%s %s %s %m %d %s %s",__FILE__,__func__,__VA_ARGS__,__LINE__,__DATE__,__TIME__); ? ? ? ? #define error(...) printf("%s %s %s %m %d %s %s",__FILE__,__func__,__VA_ARGS__,__LINE__,__DATE__,__TIME__); ? ?? ? ? 作业1:实现一个交换两个变量的宏函数,要求尽可能的通用,能使用多少种方法 ? ? ? ? swap(num1,num2)
?
|