声明(叠甲):鄙人水平有限,本文章仅供参考。


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() 函数的更多相关文章

  1. LESS详解之函数(四)

    之前已经为大家介绍了一些LESS函数,大家应该对之前介绍的有所了解了.下面依旧为大家介绍LESS的函数,附加着一些小例子.希望这些有关LESS的函数能在大家编写LESS的时候有所帮助. saturat ...

  2. 详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别

    详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别 http://blog.sina.com.cn/s/blog_686999de0100jgda.html   实例: ...

  3. ViewPager 详解(二)---详解四大函数

    前言:上篇中我们讲解了如何快速实现了一个滑动页面,但问题在于,PageAdapter必须要重写的四个函数,它们都各有什么意义,在上节的函数内部为什么要这么实现,下面我们就结合Android的API说明 ...

  4. 详解JMeter函数和变量(转载)

    详解JMeter函数和变量(1) JMeter函数可以被认为是某种特殊的变量,它们可以被采样器或者其他测试元件所引用.函数调用的语法如下: ${__functionName(var1,var2,var ...

  5. 详解python函数的参数

    详解python函数的参数 一.参数的定义 1.函数的参数在哪里定义 在python中定义函数的时候,函数名后面的括号里就是用来定义参数的,如果有多个参数的话,那么参数之间直接用逗号, 隔开 案列: ...

  6. 详解Python函数参数定义及传参(必备参数、关键字参数、默认可省略参数、可变不定长参数、*args、**kwargs)

    详解Python函数参数定义及传参(必备参数.关键字参数.默认可省略参数.可变不定长参数.*args.**kwargs) Python函数参数传参的种类   Python中函数参数定义及调用函数时传参 ...

  7. C++学习45 流成员函数put输出单个字符 cin输入流详解 get()函数读入一个字符

    在程序中一般用cout和插入运算符“<<”实现输出,cout流在内存中有相应的缓冲区.有时用户还有特殊的输出要求,例如只输出一个字符.ostream类除了提供上面介绍过的用于格式控制的成员 ...

  8. Knowledge Point 20180303 详解main函数

    学习Java的朋友想来都是从HelloWorld学起的,那么想来都对main函数不陌生了,但是main函数究竟是怎么回事呢?main函数中的参数是做什么的呢?main函数为什么能作为程序的入口呢?可不 ...

  9. 详解 pthread_detach()函数

    pthread_t 类型定义: typedef unsigned long int pthread_t; //come from /usr/include/bits/pthread.h 用途:pthr ...

  10. 详解calc()函数功能

    calc()对大家来说,或许很陌生,不太会相信calc()是css中的部分.因为看其外表像个函数,既然是函数为何又出现在CSS中呢?这一点也让我百思不得其解,今天有一同事告诉我,说CSS3中有一个属性 ...

随机推荐

  1. Spectracom 默认口令

    网络空间搜索: FoFa 找到页面: 默认口令 在github上去找 登陆成功 End!!!

  2. FPGA串口 波特率的计数器值

    开发板时钟为50Mhz, t为 20ns; xxx波特率时指每秒传xxx bit字节数据.也就是T=1/xxx; 再用T/t就可以得出波特率的计数周期了: 例如9600:T=1/96000=1.041 ...

  3. vue表格拖拽使用Sortable插件库

    1 <template > 2 <el-table 3 row-key="name" 4 :data="tableData" 5 stripe ...

  4. java pta第二次阶段性总结

    一.前言 经过这三次的pta训练,我对java再一次有了一个新的认识,这三次比起之前难度更大,所涉及的知识点更多.第4.5次作业是在前几次作业上的再次拓展,由三角形拓展到四边形,再由四边形拓展到五边形 ...

  5. axios使用总结

    一.请求配置 // 引入import axios from 'axios';import qs from 'qs';this.$axios({ method:"get", // g ...

  6. 如何在微信小程序中使用ECharts图表

    在微信小程序中使用ECharts 1. 下载插件 首先,下载 GitHub 上的 ecomfe/echarts-for-weixin 项目. 下载链接:ecomfe/echarts-for-weixi ...

  7. MAC读写模式自动挂载硬盘/不自动挂载硬盘

    一.卸载硬盘 sudo umount /dev/disk1s1 自己从磁盘工具获取设备ID 或使用终端命令:diskutil list 来获取 二.新建文件夹以供挂载,位子自选 sudo mkdir ...

  8. 关于uniapp的事件监听,使用uni.$once和uni.$on导致的重复监听

    最近写项目的时候遇到个问题,就是在使用uniapp的事件监听器时出现重复监听问题.一开始我是用的uni.$on去监听事件,然后出现了重复的触发监听.百度了下,官方提示单次触发的建议使用uni.$onc ...

  9. win 子系统导入centos7

    之前在应用商店安装过ubuntu的,有钱的建议从商店购买 window配置 , 准备一个centos系统,我是从已有系统导出的,导出命令 tar -cvf ./centos.tar ./ --excl ...

  10. STM32任意引脚模拟IIC

    关于模拟I2C,任意接口都可模拟(未全部测试,可能存在特殊情况). 关于SDA_IN与SDAOUT:如下定义: 举例:#define MPU_SDA_IN() {GPIOA->CRL&= ...