printf的实现原理
printf的声明
int _cdecl printf(const char* format, …);
_cdecl是C和C++程序的缺省调用方式
_CDEDL调用约定:
1.参数从右到左依次入栈
2.调用者负责清理堆栈
3.参数的数量类型不会导致编译阶段的错误
对于x86而言,栈向下生长,函数参数从右向左入栈,因此从第一个固定参数(format)地址向前(向上)移动就可得到其他变参的地址。
va_list相关宏(VC++中stdarg.h里x86平台的宏定义)
typedef char * va_list;
//_INTSIZEOF(n)宏:将sizeof(n)按sizeof(int)对齐。
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
//取format参数之后的第一个变参地址,4字节对齐
#define va_start(va_list ap, format) ( ap = (va_list)&format+ _INTSIZEOF(format) )
//对type类型数据,先取到其四字节对齐地址,再取其值
#define va_arg(va_list ap,type)
( *(type*)((ap += _INTSIZEOF(type)) -_INTSIZEOF(type)) )
#define va_end(va_list ap) ( ap = (va_list)0 )
如何得到参数个数?
其实printf并不知道参数个数,它只是逐个解析format字符串。对于特定类型%,使用va_arg去取相应参数的值,直到遍历字符串结束。类似于如下代码:
#include <stdio.h>
#include <stdarg.h>
void myprintf(const char *format, ...)
{
va_list ap;
char ch;
va_start(ap, format);
while(ch = *format++)
{
switch(c)
{
case 'c':
{
char ch1 = va_arg(ap, char);
putchar(ch1);
break;
}
如下调用会打印什么内容?
printf("%x");
----------------
背景知识:
函数调用栈帧结构:

比如Z调用A,Z的栈帧:
1.自右向左(约定)压入参数
2.Z中返回地址。即从A返回后Z中下一条指令地址
3.调用者的EBP。由编译器插入指令实现:
"pushl %ebp"
"movl %esp, %ebp"//esp为栈指针
因而形成一个链表。依此可得到调用者的栈顶位置(对于A的EBP,得到Z的EBP地址为0x000f)。
4.局部变量。
对于两个正整数 x, n 总存在整数 q, r 使得
x = nq + r, 其中 0<= r <n //最小非负剩余
q, r 是唯一确定的。q = [x/n], r = x - n[x/n]. 这个是带余除法的一个简单形式。在 c 语言中, q, r 容易计算出来: q = x/n, r = x % n.
所谓把 x 按 n 对齐指的是:若 r=0, 取 qn, 若 r>0, 取 (q+1)n. 这也相当于把 x 表示为:
x = nq + r', 其中 -n < r' <=0 //最大非正剩余
nq 是我们所求。关键是如何用 c 语言计算它。由于我们能处理标准的带余除法,所以可以把这个式子转换成一个标准的带余除法,然后加以处理:
x+n = qn + (n+r'),其中 0<n+r'<=n //最大正剩余
x+n-1 = qn + (n+r'-1), 其中 0<= n+r'-1 <n //最小非负剩余
所以 qn = [(x+n-1)/n]n. 用 c 语言计算就是:
((x+n-1)/n)*n
若 n 是 2 的方幂, 比如 2^m,则除为右移 m 位,乘为左移 m 位。所以把 x+n-1 的最低 m 个二进制位清 0就可以了。得到:
(x+n-1) & (~(n-1))
printf的实现原理的更多相关文章
- C语言中的可变参数-printf的实现原理
C语言中的可变参数-printf的实现原理 在C/C++中,对函数参数的扫描是从后向前的.C/C++的函数参数是通过压入堆栈的方式来给函数传参数的(堆栈是一种先进后出的数据结构),最先压入的参数最后出 ...
- 实现简单的printf函数
首先,要介绍一下printf实现的原理 printf函数原型如下: int printf(const char* format,...); 返回值是int,返回输出的字符个数. 例如: int mai ...
- printf命令详解
基础命令学习目录首页 本文是Linux Shell系列教程的第(八)篇,更多shell教程请看:Linux Shell系列教程 在上一篇:Linux Shell系列教程之(七)Shell输出这篇文章中 ...
- C语言printf()函数具体解释和安全隐患
一.问题描写叙述 二.进一步说明 请细致注意看,有例如以下奇怪的现象 int a=5; floatx=a; //这里转换是没有问题的.%f打印x是 5.000000 printf("%d\n ...
- S3C2440—6.串口的printf实现
文章目录 一.框架 二.printf函数原理 2.1 printf的声明 2.2 参数解读 2.3 如何得到可变参数的值 2.4 解决变参的宏定义 2.5 完成printf函数的封装 三.结合UART ...
- C可变参数的函数
我们实现一个简单的printf函数(可变参数) #include <stdio.h> #include <stdarg.h> void myprintf(const char ...
- 写一个简单的配置文件和日志管理(shell)
最近在做一个Linux系统方案的设计,写了一个之前升级服务程序的配置和日志管理. 共4个文件,服务端一个UpdateServer.conf配置文件和一个UpdateServer脚本,客户端一个Upda ...
- C数据结构-栈和队列,括号匹配举例---ShinePans
1.栈和队列是两种特殊的线性表 运算操作被限定仅仅能在表的一端或两端插入,删除元素,故也称它们为限定的线性表结构 2.栈的基本运算 1).Stackinit(&s) 构 ...
- 【APUE】Chapter5 Standard I/O Library
5.1 Introduction 这章介绍的standard I/O都是ISOC标准的.用这些standard I/O可以不用考虑一些buffer allocation.I/O optimal-siz ...
随机推荐
- requests模块(get请求)篇
- HTTP for Humans,更简洁更友好- 继承了urllib的所有特征- 底层使用的是urllib3- 开源地址: https://github.com/requests/requests- ...
- 引入jar包到本地仓库方法
1. 将jar放到本地某个位置,然后cmd到目标位置:2. 执行mvn install:install-file -DgroupId=com.alipay -DartifactId=alipay-tr ...
- Java 面试-即时编译( JIT )
当我们在写代码时,一个方法内部的行数自然是越少越好,这样逻辑清晰.方便阅读,其实好处远不止如此,通过即时编译,甚至可以提高执行时的性能,今天就让我们好好来了解一下其中的原理. 简介 当 JVM 的初始 ...
- 基于常规DNS隧道进行的tcp端口转发dns2tcp的使用
0x01 安装Dns2TCP dns2tcp 是一个利用DNS隧道转发TCP连接的工具,使用C语言开发. sudo apt-get install dns2tcp 0x02配置dns2tcp 配置DN ...
- How to Compute The Derivatives (如何求导数)(TBC)
A video by 3Blue1Brown in Bilibili\text{A video by 3Blue1Brown in Bilibili}A video by 3Blue1Brown in ...
- 【Spring Cloud】服务容错保护:Hystrix(四)
一.雪崩效应 在微服务架构中,由于服务和服务之间可以互相调用,一项工作的完成可能会依赖调用多个微服务模块,但由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就 ...
- libevent环境搭建
环境 VS2017.ubuntu16.04 官网网址:http://www.linuxfromscratch.org/blfs/view/svn/basicnet/libevent.html 下载 ...
- JetBrains系列软件激活码
T3ACKYHDVF-eyJsaWNlbnNlSWQiOiJUM0FDS1lIRFZGIiwibGljZW5zZWVOYW1lIjoi5bCP6bifIOeoi+W6j+WRmCIsImFzc2lnb ...
- C#的集合类型及使用技巧
在日常开发过程中,我们不能避免的要对批量数据处理,这时候就要用到集合.集合总体上分为线性集合和非线性集合.线性集合是指元素具有唯一的前驱和后驱的数据结构类型:非线性集合是指有多个前驱和后驱的数据结构类 ...
- 【python数据分析实战】电影票房数据分析(一)数据采集
目录 1.获取url 2.开始采集 3.存入mysql 本文是爬虫及可视化的练习项目,目标是爬取猫眼票房的全部数据并做可视化分析. 1.获取url 我们先打开猫眼票房http://piaofang.m ...