php怎么做直播网站吗,wordpress标签管理,广州在线网站制作公司,手机搭建网站教程#x1f517; 《C语言趣味教程》#x1f448; 猛戳订阅#xff01;#xff01;#xff01; —— 热门专栏《维生素C语言》的重制版 ——
#x1f4ad; 写在前面#xff1a;这是一套 C 语言趣味教学专栏#xff0c;目前正在火热连载中#xff0c;欢迎猛戳订阅#… 《C语言趣味教程》 猛戳订阅 —— 热门专栏《维生素C语言》的重制版 —— 写在前面这是一套 C 语言趣味教学专栏目前正在火热连载中欢迎猛戳订阅本专栏保证篇篇精品继续保持本人一贯的幽默式写作风格当然在有趣的同时也同样会保证文章的质量旨在能够产出 有趣的干货 本系列教程不管是零基础还是有基础的读者都可以阅读可以先看看目录 标题前带星号 (*) 的部分不建议初学者阅读因为内容难免会超出当前章节的知识点面向的是对 C 语言有一定基础或已经学过一遍的读者初学者可自行选择跳过带星号的标题内容等到后期再回过头来学习。值得一提的是本专栏 强烈建议使用网页端阅读 享受极度舒适的排版你也可以展开目录看看有没有你感兴趣的部分希望需要学 C 语言的朋友可以耐下心来读一读。最后可以订阅一下专栏防止找不到。 有趣的写作风格还有特制的表情包而且还干货满满太下饭了
—— 沃兹基硕德
目录
Ⅰ. 输入和输出Input Output
0x00 引入I/O 的概念
0x01 标准 I/O 流
0x01 回顾标准输入输出库 stdio.h
0x02 printf 函数初探
0x03 scanf 函数初探
0x04 常见报错 C4996scanf 可能不安全
Ⅱ. 标准输出stdout
0x00 什么printf 函数居然有返回值
0x01 探索 printf 函数 “原型”
0x02 printf 支持格式化宽度 %xd
0x03 printf 浮点数精度控制
0x04 sprintf 函数
* 0x05 fprintf 函数
Ⅲ. 标准输入stdin
0x00 scanf 的返回值
0x01 scanf 自动跳过空白字符的 “特性”
* 0x02 缓冲区问题
* 0x03 scanf 函数安全性问题探讨 0x04 sscanf 函数 * 0x05 fscanf 函数 Ⅰ. 输入和输出Input Output
0x00 引入I/O 的概念 计算机中的输入和输出简称 其中 代表 Input即输入。 代表 Output即输出。 IO 是指计算机系统与外部世界进行信息交流和数据传输的过程。
输入是指将外部信息引入计算机系统而输出是将计算机系统处理后的信息传递回外部世界。
其本质是 计算机与外部世界之间的信息交流和数据传输过程。 0x01 标准 I/O 流 C 语言中标准 I/O 流为 stdin 和 stdout
它们分别用于标准输入和标准输出stdin 就是输入可以从键盘读取用户输入的内容 再利用 stdout 输出将结果打印到屏幕上
对于 stdin 和 stdout 的具体知识点我们将通过 printf 和 scanf 函数来展开讲解
I/O 流的存在使得 C 程序可以与用户进行交互并通过控制台窗口进行输入和输出。
值得一提的是在标准 C 库中stdin 和 stdout 是已经定义好的流无需额外的设置或配置。
此外还有一个标准错误流 stderr用于将错误消息输出到屏幕我们以后会讲解。 0x01 回顾标准输入输出库 stdio.h
这里我们再回顾一下 C 标准库 stdio.h其全名为 Standard Input/Output Library
我们在第一章就介绍过该库了既然本章我们展开学习输入输出我们就重提一下 是 C 语言中用于处理输入和输出操作的核心库之一C语言本身是不自带输入输出的函数的。
之所以叫做 stdio 是因为 standard input output而它表示了这库中最经典的两个函数
printf标准输入函数inputscanf标准输出函数output
下面我们就先来介绍一下这两个函数它们分别用来输入和输出 0x02 printf 函数初探
在第一章中我们就简单介绍过这个函数了我们在写第一个程序 HelloWorld 时就用到了它
#include stdio.hint main(void)
{printf(Hello, World!\n);return 0;
}
在使用 printf 函数之前要添加 stdio.h 头文件因为 printf 函数并不是 C 语言本身自带的。
对应了标准输出流 stdout 代码演示printf 函数的用法
#include stdio.hint main(void)
{printf(Hello,World!\n);int a 100;printf(a%d, a);return 0;
} 运行结果如下 我们来看一下这个函数的 原型
int printf(const char* format [, argument] ...); 0x03 scanf 函数初探
对于输入我们可以使用 C 标准库 stdio 中的 scanf 函数针对标准输入流 stdin。 代码演示使用 scanf 接收用户输入的数据
#include stdio.hint main(void) {int a 0;int b 0;scanf(%d %d, a, b);printf(a%d, b%d\n, a, b);return 0;
} 运行结果如下假设用户输入 10 20 其中 符号代表取地址因为要读取数据所以需要知道数据被存到了哪里。 这里不带 则会 warning C4477: “scanf”: 格式字符串“%d”需要类型“int *”的参数但可变参数 1 拥有了类型“int”。
这里学完指针之后就能很好地理解了对于初学者理解取地址的概念还是比较困难的。
scanf 函数需要使用 操作符来获取变量的地址因为它需要知道在内存中存储用户输入的值的确切位置。
当你在 scanf 函数中使用一个变量作为参数时你需要告诉 scanf 函数该变量在内存中的位置以便将输入的值存储到正确的地方。这是通过使用 操作符来获取变量的地址来实现的。
例如如果你有一个整数变量 x要在 scanf 中读取它的值我们就需要
scanf(%d, x);
现在实在理解不了也没有关系后续再回来理解即可 0x04 常见报错 C4996scanf 可能不安全
error C4996: scanf: This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
呵呵这是 VS 编译器的安全提示告诉我们输入的函数是不安全的臭名昭著的 C4996
大致意思就是告诉你我 VS 觉得这个函数是不安全的建议你用更安全的函数于是乎......
VS 就推出了像 scanf_s 这样的函数搞出了一揽子 _s 的版本美名其曰 安全版本的函数。
所以scanf_s 并不是标准 C 语言提供的而是 VS 编译器提供的。
但考虑到代码跨平台比如跨到 GCC 那就会无法识别这牺牲了代码的跨平台性和可移植性。
仅个人观点我觉得这完全是脱裤子放屁弊大于利的
不想看到这种提示或不想听它喋喋不休的安全警告解决方案也是多的一笔雕凿。
最常见的就是代码开头直接加上 #define _CRT_SECURE_NO_WARNINGS 请阁下直接 CV
#define _CRT_SECURE_NO_WARNINGS
不想每次创建新源文件都复制想一劳永逸可以在 VS 安装路径下搜索 newcfile.cpp 文件
在文件开头添加这行代码如此一来每次创建新的源文件就会自动添加这玩意了。
还可以通过取消勾选安全开发生命周期 (SDL) 检查来解决方法有很多这里就不多哔哔赖赖了。 Ⅱ. 标准输出stdout
0x00 什么printf 函数居然有返回值
什么printf 函数居然有返回值 打开 MSDN或者 C reference 我们可以看到
int printf(const char* format [,argument]...);
首先我们可以看到 printf 函数是有返回值的返回一个整型表示成功打印的字符数。
我们先来试试接收一下 printf 函数的返回值看看是否真的有这么个东西。 代码演示接收 printf 的返回值
#include stdio.hint main(void)
{int ret printf(Hello, World!\n);printf(%d, ret);return 0;
} 运行结果如下 这里我们使用 ret 接收了调用 printf 之后的返回值打印出来是 14我们暂且不去关注它。
我们暂时只需要知道一点就是 printf 函数是有返回值的我们也用代码证明了这一点。
当 printf 输出输错错误时会返回负值。
因此我们可以通过检查 printf 的返回值来检测打印是否成功返回值非负即打印成功。
通常情况下我们并不需要关心它的返回值因为 printf 函数在大多数情况下都会成功打印。
但在某些情况下例如输出到一个已关闭的文件流或内存缓冲区已满的情况下可能会失败。 代码演示判断 printf 函数是否打印成功
int res printf(Hello, World!\n);if (res 0) {printf(printf 成功打印了 %d 个字符。\n, result);
}
else {printf(printf 打印失败。\n);
} 0x01 探索 printf 函数 “原型”
int printf(const char* format [,argument]...);
我们继续观察其中 const char *format 是一个字符串参数用于定义输出的格式。
包含了普通文本字符和格式控制符格式控制符以百分号%开头后面跟着一个字符
表示将要输出的数据类型如整数、浮点数、字符串等以及如何格式化这些数据。
后面的 ... 表示 printf 函数可以接受任意数量的参数。
这些参数将与格式字符串中的格式控制符匹配每个参数对应一个格式控制符比如
#include stdio.hint main(void)
{int a 10;double b 3.14159;printf(%d %f\n, a, b);return 0;
}运行结果如下 0x02 printf 支持格式化宽度 %xd
你可以在格式控制符中指定字段的宽度以控制输出的对齐和填充 %xd
比如 %5d 表示输出一个宽度为 5 的整数字段如果数字少于 5 位数会在前面用空格进行填充。
printf(%5d\n, 42); 0x03 printf 浮点数精度控制
对于浮点数而可以使用精度控制 .x 来限制小数点后面的位数
printf(%.2lf\n, 3.14159);
其中.2 表示保留两位小数那么 printf 的结果将会是 3.14。 0x04 sprintf 函数 头文件#include stdio.h 作用把一个格式化的数据转换成字符串。 MSDN介绍sprintf - C Reference 代码演示sprintf 的用法
#include stdio.hstruct S {char arr[10];int age;float f;
};int main(void) {struct S s { hello, 20, 3.14f };char buffer[100] { 0 }; // 用于存放sprintf(buffer, %s %d %f, s.arr, s.age, s.f); // 把这些信息放到buffer中了printf(%s\n, buffer); // 将buffer打印出来return 0;
} 运行结果如下 * 0x05 fprintf 函数 头文件#include stdio.h 针对所有输出流的格式化输出语句 - stdout / 文件 MSDN介绍fprintf - C Reference 代码演示随便创建一个文件在文件中写入一段话
#include stdio.hchar data[] Hey, nice to meet you~;int main(void) {FILE* pf fopen(test1.txt, w);if (pf NULL) {perror(fopen);return 1;}// 使用fprintf写文件fprintf(pf, %s, data);fclose(pf);pf NULL;return 0;
} 代码成功运行 Ⅲ. 标准输入stdin
0x00 scanf 的返回值
我们还是来看看 scanf 的 原型
int scanf(const char* format [,argument]... );
我们可以看到scanf 函数和 printf 函数一样scanf 函数也是有返回值的。
如果 scanf 成功读取了一个数据项 (按照格式字符串中的格式要求) 则返回值为 1。这表示成功读取了一个数据项并且该数据项已存储在相应的变量中。如果 scanf 未能成功读取任何数据项即输入与格式字符串不匹配或者遇到了 EOF则返回值为 0。如果在读取过程中发生错误如无法打开文件或格式字符串中的格式不正确返回值通常为 EOF-1。
对于具有多个格式化指令的 scanf 语句返回值将是成功读取的数据项数量的总和。
举个例子如果 scanf 语句包含两个 %d 格式化指令而且成功读取了两个整数返回值将是 2。 0x01 scanf 自动跳过空白字符的 “特性”
scanf 函数在 读取非字符串数据类型时会自动跳过空白字符 (空格、制表符、换行符等) 。
这意味着它会忽略输入中的空格等字符直到找到一个非空白字符或达到格式字符串的结束。 举个例子scanf 自动跳过空白字符
int num1, num2;
scanf(%d %d, num1, num2);
用户可以在两个整数之间输入任意数量的空格、制表符或换行符
scanf 都会正确读取这两个整数可以自己放到编译器里自己运行输入试试。
值得注意的是虽然 scanf 会跳过空白字符但它并不会在格式化指令中的空格之间进行跳过。
例如如果格式字符串为 %d%d那么输入中的空白字符将不会被跳过会与格式精确匹配。 * 0x02 缓冲区问题
缓冲区问题在 C 语言中经常出现尤其是在使用输入函数如 scanf、gets 等时。
这个问题主要涉及到输入函数与输入缓冲区之间的交互可能会导致程序行为与预期不符。
以下是有关缓冲区问题的一些重要细节
输入缓冲区输入函数如 scanf通常会将用户输入存储在一个缓冲区中等待程序读取。这允许用户在按回车键之前输入多个字符并且输入函数只会读取一个完整的数据项。缓冲区会自动刷新将数据传递给程序。换行符回车键用户在终端输入时通常会按下回车键换行符来提交输入。这个换行符也被存储在输入缓冲区中并被看作是输入的一部分。
我们下面举一个使用 scanf 函数时出现的缓冲区问题的例子。 代码演示scanf 缓冲区问题
#include stdio.hint main(void)
{int num;printf(输入一个数字: );scanf(%d, num);printf(你输入了: %d\n, num);return 0;
}解读如果用户输入 42 然后按下回车键一切正常。但是如果用户输入 42abc 然后按下回车键scanf 将读取 42 作为整数并将 abc 保留在输入缓冲区中以供下一次输入使用这可能导致未预期的行为。 解决方案
清空输入缓冲区为了解决缓冲区问题可以使用 fflush(stdin) 来清空输入缓冲区。然而需要注意的是fflush(stdin) 不是C标准的一部分因此在不同的编译器和平台上表现可能不同且可能不是可移植的方法。另外fflush(stdin)在某些编译器中可能会导致未定义的行为因此不建议使用。换一个安全的输入方法为了避免缓冲区问题可以使用安全的输入方法如 fgets 函数读取一行输入然后解析该行。这样可以更好地控制输入并且不会留下未处理的字符在输入缓冲区中。 * 0x03 scanf 函数安全性问题探讨
scanf 函数的安全性问题主要涉及到缓冲区溢出和格式字符串漏洞。
首先是 缓冲区溢出这个我们在刚才已经介绍过了。scanf 函数不提供对输入缓冲区大小的检查和限制这意味着如果输入的数据超过了目标变量所分配的内存空间就可能导致缓冲区溢出。这种情况可能会破坏程序的内存结构导致程序崩溃或安全漏洞。
格式字符串漏洞格式字符串参数在 scanf 中是非常强大的但也容易受到恶意用户输入的攻击。如果用户能够控制格式字符串就可以进行格式字符串漏洞攻击可能导致程序信息泄漏、崩溃或被入侵。因此应该避免使用来自用户的未经验证的格式字符串。
char format[20];
scanf(%s, format); // 恶意用户可以输入恶意格式字符串
scanf(format); // 安全漏洞用户可以控制程序行为
为了避免格式字符串漏洞应该避免将用户输入直接用作格式字符串或者使用格式化函数如printf时进行严格的格式化控制。
未处理的错误scanf 函数在输入不匹配格式字符串的情况下会返回失败但它通常不提供足够的错误信息来帮助程序员精确定位问题。这可能导致难以调试的问题尤其是在复杂的输入和格式字符串组合中。
缺乏输入验证scanf 不提供输入验证功能因此程序员需要自行验证用户输入以确保输入数据满足预期的条件。如果未进行适当的输入验证可能会导致不安全的输入数据被接受。
因此为了提高程序的安全性和可靠性应该采取以下措施
使用带有长度限制的输入函数如 fgets以避免缓冲区溢出。不直接使用来自用户的输入作为格式字符串或者对格式字符串进行有效的验证。在使用 scanf 时必要时可检查其返回值以确保成功读取了所需的数据项。进行严格的输入验证以确保输入数据满足预期的条件防止不安全的输入被接受。在可能的情况下使用更安全的输入函数或库如 strtok、strtol 等以减少潜在的安全风险。 0x04 sscanf 函数
int sscanf (const char* buffer, const char* format [, argument ] ...)
sscanf 函数比 scanf 多了一个 buffer。 头文件#include stdio.h 作用从一个字符串中读取一个格式化的数据。 MSDN介绍sscanf - C Reference 代码演示利用 sscanf 从 buffer 字符串中还原出结构体的数据
#include stdio.hstruct S {char arr[10];int age;float f;
};int main(void) {struct S s { hello, 20, 3.14f };struct S tmp { 0 };char buffer[100] { 0 };sprintf(buffer, %s %d %f, s.arr, s.age, s.f); // 把这些信息放到buffer中了printf(%s\n, buffer);// 从buffer字符串中还原出一个结构体数据sscanf(buffer, %s %d %f, tmp.arr, (tmp.age), (tmp.f));printf(%s %d %f\n, tmp.arr, tmp.age, tmp.f);return 0;
}运行结果如下 * 0x05 fscanf 函数
int fscanf (FILE* stream,const char* format [, argument ]... ); 头文件#include stdio.h 针对所有输入流的格式化输入语句 - stdin / 文件 MSDN介绍fscanf - C Reference 代码演示fscanf 的用法 #include stdio.hint data; // 存放读到的数据int main(void) {FILE* pf fopen(test.txt, r);if (pf NULL) {perror(fopen);return 1;}// 使用fscanf读文件fscanf(pf, %d, data);// 将读到的数据打印printf(%d\n, data);fclose(pf);pf NULL;return 0;
}运行结果如下 [ 笔者 ] 王亦优 | 雷向明[ 更新 ] 2023.3.
❌ [ 勘误 ] /* 暂无 */[ 声明 ] 由于作者水平有限本文有错误和不准确之处在所难免本人也很想知道这些错误恳望读者批评指正 参考文献 - Creference[EB/OL]. []. http://www.cplusplus.com/reference/. - Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. . - 百度百科[EB/OL]. []. https://baike.baidu.com/. - 维基百科[EB/OL]. []. https://zh.wikipedia.org/wiki/Wikipedia - R. Neapolitan, Foundations of Algorithms (5th ed.), Jones Bartlett, 2015. - B. 比特科技. C/C[EB/OL]. 2021[2021.8.31] - 林锐博士. 《高质量C/C编程指南》[M]. 1.0. 电子工业, 2001.7.24. - 陈正冲. 《C语言深度解剖》[M]. 第三版. 北京航空航天大学出版社, 2019. - 侯捷. 《STL源码剖析》[M]. 华中科技大学出版社, 2002. - T. Cormen《算法导论》第三版麻省理工学院出版社2009年。 - T. Roughgarden, Algorithms Illuminated, Part 1~3, Soundlikeyourself Publishing, 2018. - J. KleinbergE. Tardos, Algorithm Design, Addison Wesley, 2005. - R. SedgewickK. Wayne《算法》第四版Addison-Wesley2011 - S. Dasgupta《算法》McGraw-Hill教育出版社2006。 - S. BaaseA. Van Gelder, Computer Algorithms: 设计与分析简介》Addison Wesley2000。 - E. Horowitz《C语言中的数据结构基础》计算机科学出版社1993 - S. Skiena, The Algorithm Design Manual (2nd ed.), Springer, 2008. - A. Aho, J. Hopcroft, and J. Ullman, Design and Analysis of Algorithms, Addison-Wesley, 1974. - M. Weiss, Data Structure and Algorithm Analysis in C (2nd ed.), Pearson, 1997. - A. Levitin, Introduction to the Design and Analysis of Algorithms, Addison Wesley, 2003. - A. Aho, J. - E. Horowitz, S. Sahni and S. Rajasekaran, Computer Algorithms/C, Computer Science Press, 1997. - R. Sedgewick, Algorithms in C: 第1-4部分第三版Addison-Wesley1998 - R. Sedgewick《C语言中的算法》。第5部分第3版Addison-Wesley2002