首页 > 其他分享 >详解 printf() 函数

详解 printf() 函数

时间:2022-10-06 18:24:27浏览次数:80  
标签:... num 函数 int fmt ret 详解 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 来玩玩?但是记得按照手册的描述来!

标签:...,num,函数,int,fmt,ret,详解,printf
From: https://www.cnblogs.com/luokeIT/p/16758140.html

相关文章

  • sorted函数中的key
    sorted函数有三个参数:iterable、key、reverse。其中,iterable为一个可迭代的对象,reverse表示是否对排序结果进行反转,而key稍微复杂一点,主要是用来对需要比较的元素进行处理......
  • 概率生成函数 (PGF)
    1.概述取值处概率的生成函数。\(F(1)=1,F'(1)=E\)2.分析设\(F(i)\)为\(i\)时刻结束概率的生成函数,\(G(i)\)为\(i\)时刻未结束概率的生成函数,那么有:\[f_i+g_i=g......
  • MySQL存储引擎详解(一)-InnoDB架构
    前言目前MySQL8.x版本数据库已经支持了很多存储引擎了,但是一般我们常用的就几种,容易形成思维固化不会轻易采取其他存储引擎,从而错失很多优化存储的功能。因此对现支持的九种......
  • 算法学习笔记(数学):数论分块 + 容斥原理 + 莫比乌斯函数
    算法学习笔记(数学):数论分块+容斥原理+莫比乌斯函数这篇文章主要是要讲一道题目(链接在这里)以及梳理一下数论分块,莫比乌斯函数,容斥原理这些知识。先介绍下知识点吧qwq......
  • 滚动元素Marquee用法详解
    代码如下:<marquee><fontsize=+3color=red>Hello,World</font></marquee>下面这两个事件经常用到:onMouseOut="this.start()" :用来设置鼠标移出该区域时继续滚动onMouse......
  • MySQL存储引擎详解(一)-InnoDB架构
    前言目前MySQL8.x版本数据库已经支持了很多存储引擎了,但是一般我们常用的就几种,容易形成思维固化不会轻易采取其他存储引擎,从而错失很多优化存储的功能。因此对现支持的九种......
  • 最短路径问题---Dijkstra算法详解
    0.最短路径问题介绍问题解释:从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径1.Dijkstra算法介绍算法特点:迪科斯彻算法使用......
  • 8.函数上
    函数函数的原型和调用在使用函数前必须定义或者声明函数doublecircle(doubler);intmain(){ doublelength=circle(10);printf("length=%f\n",length......
  • C语言:三角函数的参数为弧度,通常的角度值需要转化为弧度
    #include<stdio.h>#include<math.h>//三角函数的参数为弧度,是角度必须转化为弧度//3.14=180,1度=3.14/180,转化方法:(3.14/180)*角度值main(){floata,b,c;......
  • 对于函数递归的理解
    递归的代码操作就是在自己未完成的函数之中调用自己这样看起来是并不合理的,因为在一个为完成的东西之中使用他自己,是不太现实的但是如果从代码执行的逻辑来进行理解的话,......