| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> C++知识库 -> 【纯干货】深度剖析C语言,你真的懂printf吗? -> 正文阅读 |
|
[C++知识库]【纯干货】深度剖析C语言,你真的懂printf吗? |
注意本文为针对C语言的基础技术向,更适合刚入门需要进阶的同学技术进阶使用,当然我会尽量用简单的语言让哪怕是初学者也可以看得懂,核心知识有如下部分:
视频本文可以结合视频一起食用,效果更佳:视频链接 再观printf函数相信绝大部分同学写的第一个程序,应该就是大名鼎鼎的 在C语言中,这个程序一般写出来是这个样子的:
如果在命令行运行这个程序的话,你将获得一行 如果你是一个不喜欢思考的初学者,可能会认为printf也许是C语言里面最基础最简单的函数了,毕竟这个程序这么简洁、格式这么规范、使用这么简单、甚至闭着眼睛都不会写错。 但如果我们往细究往深了想呢?printf它是如何实现的呢?我们是否可以实现一个自己的printf函数呢?为什么它会运行在命令行中而不是从屏幕某个规定的位置显示呢…… 这时你可能会发现,woc,printf似乎比我们想象的要复杂太多了,你以为的简单小函数,其实却是个超级大boss。不过不要担心,我们会从基础开始讲,而且我会保证尽量说得简单明了。 print为什么要加个f现在,我们重新抱着认真学习的态度来观察printf这个函数吧。首先我们从名字来解析一下。 我们知道,在英语中,print有打印、印刷的意思,这个词语完全可以解释将字符串打印到电脑屏幕上这个过程,那么为什么这个函数的名字要加上f呢? 其实这个f是format的缩写,即格式化的意思,printf其实为格式化打印的意思。那么格式化这个是怎么来的? 现在想想,如果我们希望在屏幕上输出一个变量该怎么做呢?最简单的方法就是使用格式字符,就如下面这个程序一样:
这个函数将会在屏幕上输出一行 现在从这个功能中我们其实可以发现很多问题,我们来逐一解释: 格式符是什么时候被解析的?这个问题是我在学习理解printf的时候第一个思考的问题,即格式符是如何什么时候被解析的,答案有两种,对应了两种不同的结果。 首先是在编译阶段被解析,这种方式的话也有两种情况,一是编译器有针对printf这类函数的优化,二是编译器会主动把字符串中的格式符变成某种执行效果。而另一种方案是printf函数内部实现的解析格式符。 我们可以用如下的程序来测试(为了方便,后面我们生成程序的名称都为demo.exe):
然后我们把它们编译完成后,在命令行中使用 最后我们会得到两行输出 对于上面的程序,我们来理解一下: 其中首先是main函数的部分,可以发现我们在里面写了两个参数,其实这种写法才是C语言main函数的标准写法,具体可以参考我之前写的《C的main函数解析》这篇博客,这里不作详述,这里只说一下其中第八行的 既然知道了这个,那么printf解析的方案其实也就很容易出来了,程序通过检测百分号 如何实现变参函数既然知道了如何解析格式符,那么我们似乎就可以实现printf函数了?可是等一下,我们在写printf的时候为什么可以写多个参数? 一般而言,如果我们仅仅跟着学校学习的那点C语言知识,肯定不会讲什么变参函数的,但是printf这个函数的存在却离不开变参函数这个概念。而且,配合变参函数,你还可以实现一些类似于其它更加高级语言的重载、默认参数等功能。 如果需要实现变参函数,那么有一个库文件就很关键了,那就是
通过这个函数我们即可以实现一个变参的加法。我们来看一下这里的写法,其实很简单,除了格式要求在函数的形参列表末尾加上三个点表示这是个变参函数外,重点也就一个数据类型和三个函数。 不过为了防止看不懂,我们来从原理上理解一下这个功能。(此部分仅作参考,我并没有实际去看编译器的内容,而源文件也被隐藏起来了,故下面的内容大多是基于我自己的分析)。 首先是三个点的作用,很显然这个东西是给编译器看的,因为如果不加三个点,那么我们使用函数的时候,实参与形参个数对不上,编译器就会报错,而这三个点就可以告诉编译器,这是个变参函数,实参多了少了你别叫唤。 然后是一个数据类型即 而剩下三个函数中,第一个调用的是 然后是第二个函数,在程序中被反复调用的 最后第三个函数,我们可以调用 除此之外,其实stdarg中还有很多其它的函数,不过我们平常不大用得到,如果感兴趣的话可以自行研究研究。 现在,我们来试试刚刚写的程序吧,在主函数中如此调用:
最后我们可以得到一个 来实现一个printf吧现在我们来实现一个printf吧,为了方便我用比较简单潦草的程序只写了一个
上面这个程序其实你们自己都可以写,如果还是有点不懂的话可以仔细读一下,里面用的函数都是非常基础的API,现在我们测试一下:
这个程序可以用来测试MyPrint的以下功能:
现在,只要我们继续填充内容,就可以实现自己的printf功能了,甚至可以添加一些自己的格式符,来实现一些更加复杂高级的输出。 printf是否有检测功能我们经过刚才的研究发现,在使用printf的时候哪怕格式字符和数据的个数对不上号,我的编译器似乎也不会报错(这里仅指代MinGW,如果你使用的codeblocks或者其它开源IDE,可能结果与我一样),但是有的编译器不一样,例如针对嵌入式的 我们知道,平常我们编程的时候,编译器说什么,我们就得听什么,必须完全听从编译器的指挥。从绝大部分情况来看,它很方便也很可靠,毕竟不用动脑子按规范来就行。但是C语言是一个非常贴近硬件的语言,我们很多时候可能会完成一些匪夷所思的操作来实现某种功能,还有时候我们有些操作非常危险需要用一种方法来时刻提醒我们的大脑,那么我们就可以使用 当然, 而这里,我们同样可以使用 printf只能输出在命令行吗目前我在Windows上使用MinGW测试,暂时没有发现其printf有重定向功能,不过这个应该是为了安全考虑,毕竟你在Windows上实现在其它地方打印,一般需要使用Windows提供的API,否则很容易出问题,而如Linux这些系统,大多也是如此。 不过对于嵌入式来说,重写pritnf就是很平常很普通的任务了,我们一般会把printf重写在串口上,但是在写了屏幕驱动后,也可以把printf重新封装到屏幕上,而Windows运行的printf应该就是把printf连接在了Windows的屏幕驱动中,然后通过调用驱动的方式来实现的数据打印功能。 本质上printf其实可以当作连续按照格式使用putchar的函数,而putchar也就打印字符的函数,那么我们只要可以确定每个putchar打印的位置,那么printf的使用就迎刃而解了。 结语现在你已经基本懂得了printf是如何实现的了,那么用这个方法反推,其实你应该也可以实现scanf的功能,因为它们的思想都是大致相同的。从觉得printf是一个非常简单的函数,到觉得printf好像非常麻烦,再到我们现在将它几乎完整地剖析了一遍,现在回头看,它的实现机理其实并不算简单,但也没那么复杂,重点是要主动去思考,去想,去理解,去实验。 |
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 | -2024/12/27 4:37:09- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |
数据统计 |