详解 printf() 函数
声明(叠甲):鄙人水平有限,本文章仅供参考。
1. 引子
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
上面这一段代码大家应该都十分的熟悉,这是我们学习 C 语言时接触到的第一段代码,但问到printf这个函数是如何工作运行的、是怎么做到可变参数传递的,却没多少人能回答上来,因此我想写下这篇文章来分享下我对 printf 的认识,但是因鄙人水平有限,本文只会粗略的进行介绍。
2.可变参数传递
printf 的实现其实是十分的复杂的,我们一步一步来进行说明,首先我们来看看 printf 是如何做到可变参数传递的,即如下代码
#include <stdio.h>
int main()
{
printf("hello world\n");
printf("hello %s\n","world");
printf("%s %s\n","hello","world");
return 0;
}
我们可以看到 printf 传递的参数是可以变的,是不固定,这与我们平时接触到的的函数参数传递有亿点点的不同。
为明白这个,我先进行一波 RTFSC(看源代码)
_Check_return_opt_
_CRT_STDIO_INLINE int __CRTDECL printf(
_In_z_ _Printf_format_string_ char const* const _Format,
...);
通过代码跳转,我们可以在 stdio.h 这个文件中看到 printf 的定义,但是看起有点复杂,充满了我们不太熟悉的宏定义。为了不理会不我们不感兴趣的部分,在此我对其进行一波简化,如下
int printf(char *Format,...);
这样是不是简单多了,跟我们平时写的函数是不是很很像,返回值类型是 int ,传入参数是一个 char 的指针 Format 和一个 ... 。其中 ... 就是实现可变参数的所在之处,其具体用法和含义,因本人水平有限可能说不明白(像偷懒,就在此扔个教学链接吧。
3.printf 的内部
printf 的实现真的十分复杂,在此就以阉割版为例了。
在第二节后,我们明白了可变参数传递的具体实现原理。这节,我们就来看看 printf 是如何利用它来工作的,如下:
#include <stdarg.h>
int printf(const char *fmt, ...)
{
va_list list;
va_start(list,fmt);
int ret_num = 0;
for(;fmt[ret_num] != '\0';ret_num++)
{
switch (fmt[ret_num])
{
case '%':
ret_num++;
switch (fmt[ret_num])
{
case 'c':
Argchar = va_arg(list,int);
putch(Argchar);
break;
default:
ret_num--;
break;
}
break;
default:
putch(fmt[ret_num]);
break;
}
}
return ret_num;
}
int main()
{
printf("char = %c\n",'C');
return 0;
}
这是一个阉割版的 printf,只能实现 %c 的传递,但是不凡我们以此为突破口来弄得 printf 的具体实现。
如上,我们先来看看它的返回值,int 类型,大小为传递参数的个数。对于传入参数,先是对于不同的格式化字符串进行判断(即 %d %c那些,具体各个含义可以通过 man3 printf 这个命令来查看手册的描述),再使用 putch 这个函数进行单个字符的输出,而 putch 的实现就是后话了。
4.结束语
好了,对于 printf 的介绍就到这了,相信你对于 printf 的也有了进一步的了解,要不要自己写个 printf 来玩玩?但是记得按照手册的描述来!
详解 printf() 函数的更多相关文章
- LESS详解之函数(四)
之前已经为大家介绍了一些LESS函数,大家应该对之前介绍的有所了解了.下面依旧为大家介绍LESS的函数,附加着一些小例子.希望这些有关LESS的函数能在大家编写LESS的时候有所帮助. saturat ...
- 详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别
详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别 http://blog.sina.com.cn/s/blog_686999de0100jgda.html 实例: ...
- ViewPager 详解(二)---详解四大函数
前言:上篇中我们讲解了如何快速实现了一个滑动页面,但问题在于,PageAdapter必须要重写的四个函数,它们都各有什么意义,在上节的函数内部为什么要这么实现,下面我们就结合Android的API说明 ...
- 详解JMeter函数和变量(转载)
详解JMeter函数和变量(1) JMeter函数可以被认为是某种特殊的变量,它们可以被采样器或者其他测试元件所引用.函数调用的语法如下: ${__functionName(var1,var2,var ...
- 详解python函数的参数
详解python函数的参数 一.参数的定义 1.函数的参数在哪里定义 在python中定义函数的时候,函数名后面的括号里就是用来定义参数的,如果有多个参数的话,那么参数之间直接用逗号, 隔开 案列: ...
- 详解Python函数参数定义及传参(必备参数、关键字参数、默认可省略参数、可变不定长参数、*args、**kwargs)
详解Python函数参数定义及传参(必备参数.关键字参数.默认可省略参数.可变不定长参数.*args.**kwargs) Python函数参数传参的种类 Python中函数参数定义及调用函数时传参 ...
- C++学习45 流成员函数put输出单个字符 cin输入流详解 get()函数读入一个字符
在程序中一般用cout和插入运算符“<<”实现输出,cout流在内存中有相应的缓冲区.有时用户还有特殊的输出要求,例如只输出一个字符.ostream类除了提供上面介绍过的用于格式控制的成员 ...
- Knowledge Point 20180303 详解main函数
学习Java的朋友想来都是从HelloWorld学起的,那么想来都对main函数不陌生了,但是main函数究竟是怎么回事呢?main函数中的参数是做什么的呢?main函数为什么能作为程序的入口呢?可不 ...
- 详解 pthread_detach()函数
pthread_t 类型定义: typedef unsigned long int pthread_t; //come from /usr/include/bits/pthread.h 用途:pthr ...
- 详解calc()函数功能
calc()对大家来说,或许很陌生,不太会相信calc()是css中的部分.因为看其外表像个函数,既然是函数为何又出现在CSS中呢?这一点也让我百思不得其解,今天有一同事告诉我,说CSS3中有一个属性 ...
随机推荐
- bzoj 3924
动态点分治好题 首先我们考虑一个暴力做法: 每次修改之后选一个点作为根搜索整棵树,然后换根dp即可 考虑每次换根时,移向的点的消耗会减少子树代价之和*边权,而其余部分代价会增加剩余代价*边权 这样每次 ...
- K8s 与 Service Mesh(服务与流量治理)
K8s相关资料可参考链接: Kubernetes简介 1 什么是Service Mesh Service Mesh 是一个专门处理服务通讯的基础设施层.它的职责是在由云原生应用组成服务的复杂拓扑结构 ...
- vue后台管理系统——用户管理模块
电商后台管理系统的功能--用户管理模块 1. 用户管理概述 通过后台管理用户的账号信息,具体包括用户信息的展示.添加.修改.删除.角色分配.账号启用/注销等功能. 用户信息列表展示 添加用户 修改用户 ...
- python基础篇 26-redis操作
redis的基本操作: redis_conf ={ 'host':'192.168.64.128', 'password':'Aa123456', 'db':'0', 'port':6379, 'de ...
- JAVA基础Day3-用户交互Scanner/顺序结构/选择结构/循环结构
一.用户交互Scanner JAVA提供了一个工具类,可以用来获取用户的输入. java.util.Scanner是Java5的新特征,可以通过Scanner类来获取用户的输入. 基本语法: Scan ...
- idea中 .gitignore文件的使用
idea中 .gitignore文件的使用 首先保证当前的所有文件都没有被git追踪 如果被追踪,建议先取消git的版本控制 输入如下指令 find . -name ".git" ...
- 基于CMMI的软件工程第一章读书笔记
基于CMMI的软件工程第一章读书笔记 软件作为产品,就如机械业以及一般的加工业一样,只有对产品的产生流程和角色分工及其相应的管理活动有一个成熟的模式,能"更快,更好,更便宜"地开发 ...
- Java中StringBuilder相关知识点
StringBuilder 1.StringBuilder概述 如果对字符串进行拼接操作,每次拼接都会构建一个新的String对象,既耗时又浪费空间,而且这种操作还不可避免.我们可以通过StringB ...
- MySQL Workbench常用快捷键及修改方法
常用快捷键: 1. 执行整篇sql脚本:Ctrl + Shift + Enter 2. 执行当前行:Ctrl + Enter 3. 注释/取消注释: Ctrl + / 4. 格式化sql语句(美化sq ...
- C++的万能引用解析
C++11除了带来了右值引用以外,还引入了一种称为"万能引用"的语法:通过"万能引用",对某型别的引用T&&,既可以表达右值引用,也可以表达左值 ...