对printf源码的分析

对printf源码的分析

一、printf的源码如下

#include <stdio.h>
#include <stdarg.h>
 
 
//va_start(arg,format),初始化参数指针arg,将函数参数format右边第一个参数地址赋值给arg
//format必须是一个参数的指针,所以,此种类型函数至少要有一个普通的参数, 
//从而提供给va_start ,这样va_start才能找到可变参数在栈上的位置。 
//va_arg(arg,char),获得arg指向参数的值,同时使arg指向下一个参数,char用来指名当前参数型
//va_end 在有些实现中可能会把arg改成无效值,这里,是把arg指针指向了 NULL,避免出现野指针 
 
 
void print(const char *format, ...)
{
	va_list arg;
	va_start(arg, format);
 
	while (*format)
	{
		char ret = *format;
		if (ret == '%')
		{
			switch (*++format)
			{
			case 'c':
			{
						char ch = va_arg(arg, char);
						putchar(ch);
						break;
			}
			case 's':
			{
						char *pc = va_arg(arg, char *);
						while (*pc)
						{
							putchar(*pc);
							pc++;
						}
						break;
			}
			default:
				break;
			}
		}
		else
		{
			putchar(*format);
		}
		format++;
	}
	va_end(arg);
}

二、从上到下对不认识的进行分析

1.<stdarg.h>

​ stdarg.h是C标准库的文件,其名字为standard arguments 标准参数,目的主要为了让函数能够接收可变参数。

​ 实现可变参数需要至少有一个已经命名的参数 char *right(int a,…);

va_list用来保存宏va_arg和宏va_end所需要的信息

typedef struct {
char *a0; /* pointer to first homed integer argument */
int offset; /* byte offset of next parameter */
} va_list;

INTSIZEOF 宏,获取类型占用的空间长度,最小占用长度为int的整数倍:

#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

VA_START宏,获取可变参数列表的第一个参数的地址(ap是类型为va_list的指针,v是可变参数最左边的参数):

#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )

VA_ARG宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(t参数描述了当前参数的类型):

#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

VA_END宏,清空va_list可变参数列表:

#define va_end(ap) ( ap = (va_list)0 )

#include <stdio.h>
#include <stdarg.h>
void printargs(int arg1, ...) /* 输出所有int类型的参数,直到-1结束 */
{
va_list ap;//定义valist类型的变量,这个变量是指向参数的指针
int i;
va_start(ap, arg1);//初始化刚刚定义的valist变量ap是刚才的列表 arg1是第一个固定的参数
for (i = arg1; i != -1; i = va_arg(ap, int))//从第一个固定的参数开始循环找下一个int直到-1
printf("%d ", i);//输出找到的参数
va_end(ap);//结束可变参数的获取
putchar('\n');
}
int main(void)
{
printargs(5, 2, 14, 84, 97, 15, 24, 48, -1);
printargs(84, 51, -1);
printargs(-1);
printargs(1, -1);
return 0;
}

这个程序产生输出:
5 2 14 84 97 15 24 48
84 51
1

三、代码分析

让我们回到代码,print函数默认函数为const char *format,… 所以先定义va_list 然后调用宏va_start(arg,format)

当format当前值不为空,如果当前值不为%号,则将当前的符号输出然后format++遍历下一个字母,当当前值为%的时候,可以知道出现了格式化字符,则判断下一个是c还是s还是d,分别执行相应的功能。

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值