详解 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 来玩玩?但是记得按照手册的描述来!

posted @ 2022-10-06 18:06  luokesi  阅读(161)  评论(0编辑  收藏  举报