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的实现原理的更多相关文章

  1. C语言中的可变参数-printf的实现原理

    C语言中的可变参数-printf的实现原理 在C/C++中,对函数参数的扫描是从后向前的.C/C++的函数参数是通过压入堆栈的方式来给函数传参数的(堆栈是一种先进后出的数据结构),最先压入的参数最后出 ...

  2. 实现简单的printf函数

    首先,要介绍一下printf实现的原理 printf函数原型如下: int printf(const char* format,...); 返回值是int,返回输出的字符个数. 例如: int mai ...

  3. printf命令详解

    基础命令学习目录首页 本文是Linux Shell系列教程的第(八)篇,更多shell教程请看:Linux Shell系列教程 在上一篇:Linux Shell系列教程之(七)Shell输出这篇文章中 ...

  4. C语言printf()函数具体解释和安全隐患

    一.问题描写叙述 二.进一步说明 请细致注意看,有例如以下奇怪的现象 int a=5; floatx=a; //这里转换是没有问题的.%f打印x是 5.000000 printf("%d\n ...

  5. S3C2440—6.串口的printf实现

    文章目录 一.框架 二.printf函数原理 2.1 printf的声明 2.2 参数解读 2.3 如何得到可变参数的值 2.4 解决变参的宏定义 2.5 完成printf函数的封装 三.结合UART ...

  6. C可变参数的函数

    我们实现一个简单的printf函数(可变参数) #include <stdio.h> #include <stdarg.h> void myprintf(const char ...

  7. 写一个简单的配置文件和日志管理(shell)

    最近在做一个Linux系统方案的设计,写了一个之前升级服务程序的配置和日志管理. 共4个文件,服务端一个UpdateServer.conf配置文件和一个UpdateServer脚本,客户端一个Upda ...

  8. C数据结构-栈和队列,括号匹配举例---ShinePans

    1.栈和队列是两种特殊的线性表             运算操作被限定仅仅能在表的一端或两端插入,删除元素,故也称它们为限定的线性表结构 2.栈的基本运算 1).Stackinit(&s) 构 ...

  9. 【APUE】Chapter5 Standard I/O Library

    5.1 Introduction 这章介绍的standard I/O都是ISOC标准的.用这些standard I/O可以不用考虑一些buffer allocation.I/O optimal-siz ...

随机推荐

  1. Spring bean的作用域以及生命周期

    一.request与session的区别 request简介 request范围较小一些,只是一个请求. request对象的生命周期是针对一个客户端(说确切点就是一个浏览器应用程序)的一次请求,当请 ...

  2. UVA - 11400 Lighting System Design

    题文: You are given the task to design a lighting system for a huge conference hall. After doing a lot ...

  3. 从零开始实现ASP.NET Core MVC的插件式开发(六) - 如何加载插件引用

    标题:从零开始实现ASP.NET Core MVC的插件式开发(六) - 如何加载插件引用. 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/1171 ...

  4. “零基础”如何快速掌握web前端核心技术?

    前端开发要学的知识内容涉及的会很宽泛,虽然说主要是HTML.CSS和JavaScript这些基础知识点,今天想强调一下,学前端开发除了要学这些基础知识外,学员还要在这之上进行延伸和深入的去学,而且互联 ...

  5. Windows快捷方式绑马

    0x01前言 windows的shell32在处理控制面板程序的快捷方式文件时,存在一个漏洞,可以加载硬盘上的任意DLL文件,即可执行任意代码. 之前看到一款俄罗斯黑阔写的的快捷方式下载木马并运行的生 ...

  6. css涂鸦这样玩

    前言 上一次深扒CSS的时候,还说CSS和H5绘制复杂图形很麻烦,看了大神的操作后,感觉茅塞顿开了,哈哈. 就算可能我暂时没有用到的机会,学习一下开发者的设计思路也是受益匪浅呀. 嗯,今天要介绍的是一 ...

  7. 08 python学习笔记-随机生成大乐透号码(八)

    1 #产生大乐透号码 2 #前区 1-32,5 后区 1-12,2 3 #1.前区从1-32中级取5个,后区再从1-12里面取2个 4 #01 02 03 04 5 def dlt(): #生成随机大 ...

  8. $.ajax.html

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <link rel= ...

  9. chrome安装json美化软件 JSONView

    安装效果如下: 安装步骤: 1.下载地址: github地址:https://github.com/gildas-lormeau/JSONView-for-Chrome 2.解压文件 3.打开谷歌浏览 ...

  10. Display(显示) 与 Visibility(可见性)

    display属性设置一个元素应如何显示,visibility属性指定一个元素应可见还是隐藏. 隐藏元素 - display:none或visibility:hidden 隐藏一个元素可以通过把dis ...