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


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. 命令提示符怎么打开和Dos命令的基本使用

    Dos命令 Dos命令窗口我们可以通过win+R输入cmd来打开 如果要用管理员身份打开直接在开始里搜索命令提示符,以管理员身份打开! 一些简单的Dos命令 //切换盘符 盘符名: //进入 cd / ...

  2. 策略模式demo

    /** StrategyContext. */ public class StrategyContext { private Strategy strategy; // 传入的是Strategy的实现 ...

  3. 简单a+b

    1 # include <stdio.h> 2 3 /*输入两个整数a和b,计算a+b的和 4 注意此题是多组测试数据 */ 5 6 // 我的解法 7 void main(){ 8 in ...

  4. app自动化前置准备&环境搭建

    编写脚本之前的一些前置准备工作. 1,安装appium server:官网地址:http://appium.io/ 安装教程:https://www.cnblogs.com/gancuimian/p/ ...

  5. pytest之conftest.py

    一.conftest.py的特点 1.可以跨.py文件调用,有多个.py文件调用时,可让conftest.py只调用了一次fixture,或调用多次fixture 2.conftest.py与运行的用 ...

  6. Vue中nextTick的时序问题

    前言 Vue.$nextTick这个API相信很多人都用过,按照文档的解释,"在下次DOM更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的DOM".我们通 ...

  7. Jetlinks物联网基础平台 前端运行项目遇到的问题

    电脑中的环境要必备 node.js和yarn(需要将他们都添加到环境变量中,否则会报错) 1.在github上面拉取代码 $ git clone https://github.com/jetlinks ...

  8. JS笔记(三):函数与对象

    镇楼图 Pixiv:torino 四.Function类型 Rest语法 一些函数如Math.max可以支持任意数量的参数,JS中对于这样的参数可以简单使用...来实现,使用剩余参数,它支持收集剩余的 ...

  9. Verilog 变量声明与数据类型一

    Verilog 变量声明与数据类型一 Verilog语法中最基本的数据类型有 线网(wire),寄存器(reg)和整数(integer)三种类型,这三种数据类型是可综合的数据类型,在Verilog 程 ...

  10. if判断while循环