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

在C/C++中,对函数参数的扫描是从后向前的。C/C++的函数参数是通过压入堆栈的方式来给函数传参数的(堆栈是一种先进后出的数据结构),最先压入的参数最后出来,在计算机的内存中,数据有2块,一块是堆,一块是栈(函数参数及局部变量在这里),而栈是从内存的高地址向低地址生长的,控制生长的就是堆栈指针了,最先压入的参数是在最上面,就是说在所有参数的最后面,最后压入的参数在最下面,结构上看起来是第一个,所以最后压入的参数总是能够被函数找到,因为它就在堆栈指针的上方。printf的第一个被找到的参数就是那个字符指针,就是被双引号括起来的那一部分,函数通过判断字符串里控制参数的个数来判断参数个数及数据类型,通过这些就可算出数据需要的堆栈指针的偏移量了,下面给出printf("%d,%d",a,b);(其中a、b都是int型的)的汇编代码.

.section
.data
string out = "%d,%d"
push b  //最后的先压入栈中
push a //最先的后压入栈中
push $out//参数控制的那个字符串常量是最后被压入的
call printf
 
你会看到,参数是最后的先压入栈中,最先的后压入栈中,参数控制的那个字符串常量是最后被压入的,所以这个常量总是能被找到的。
 
通常情况下函数可变参数表的长度是已知的,通过num参数传入,这种函数比较容易实现。
而printf函数的实现非常复杂因为
1)可变参数的个数不能轻易的得到
2)而可变参数的类型也不是固定的,需由格式字符串进行识别(由%f、%d、%s等确定)
在这个函数中,需通过对传入的格式字符串(首地址为lpStr)进行识别来获知可变参数个数及各个可变参数的类型,具体实现体现在for循环中。譬如,在识别为%d后,做的是va_arg ( vap, int ),而获知为%l和%lf后则进行的是va_arg ( vap, long )、va_arg ( vap, double )。格式字符串识别完成后,可变参数也就处理完了。
 
 
printf的简单实现:
#include
#include void myitoa(int n, char str[], int radix)
{
int i , j , remain;
char tmp;
i = ;
do
{
remain = n % radix;
if(remain > )
str[i] = remain - + 'A';
else
str[i] = remain + '';
i++;
}while(n /= radix);
str[i] = '\0'; for(i-- , j = ; j <= i ; j++ , i--)
{
tmp = str[j];
str[j] = str[i];
str[i] = tmp;
} } void myprintf(const char *format, ...)
{
char c, ch, str[];
va_list ap; va_start(ap, format);
while((c = *format))
{
switch(c)
{
  case '%':
  ch = *++format;
  switch(ch)
  {
    case 'd':
    {
    int n = va_arg(ap, int);
    myitoa(n, str, );
    fputs(str, stdout);
    break;
    }
    case 'x':
    {
      int n = va_arg(ap, int);
      myitoa(n, str, );
      fputs(str, stdout);
      break;
    }
    case 'f':
    {
      double f = va_arg(ap, double);
      int n;
      n = f;
      myitoa(n, str, );
      fputs(str, stdout);
      putchar('.');
      n = (f - n) * ;
      myitoa(n, str, );
      fputs(str, stdout);
      break;
    }
    case 'c':
    {
      putchar(va_arg(ap, int));
      break;
    }
    case 's':
    {
      char *p = va_arg(ap, char *);
      fputs(p, stdout);
      break;
    }
    case '%':
    {
      putchar('%');
      break;
    }
    default:
    {
      fputs("format invalid!", stdout);
      break;
    }
  }
  break;
  default:
    putchar(c);
    break;
  }
  format++;
}
va_end(ap);
} int main(void)
{
myprintf("%d, %x, %f, %c, %s, %%,%a\n", , , 3.14, 'B', "hello");
return ;
}
from:http://blog.csdn.net/hackbuteer1/article/details/7558979

C语言中的可变参数-printf的实现原理的更多相关文章

  1. C语言中的可变参数函数的浅析(以Arm 程序中的printf()函数实现为例) .

    我们在C语言编程中会遇到一些参数个数可变的函数,一般人对它的实现不理解.例如Printf(): Printf()函数是C语言中非常常用的一个典型的变参数函数,它 的原型为: int printf( c ...

  2. C语言中函数可变参数解析

    大多数时候,函数中形式参数的数目通常是确定的,在调用时要依次给出与形式参数对应的所有实际参数.但在某些情况下希望函数的参数个数可以根据需要确定.典型的例子有 大家熟悉的函数printf().scanf ...

  3. C语言中的可变参数函数

    C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为: int printf( const char* format, ...); 它除了有一个参数format固定以外 ...

  4. c语言中的# ## 可变参数宏 ...和_ _VA_ARGS_ _

    1.#假如希望在字符串中包含宏参数,ANSI C允许这样作,在类函数宏的替换部分,#符号用作一个预处理运算符,它可以把语言符号转化程字符串.例如,如果x是一个宏参量,那么#x可以把参数名转化成相应的字 ...

  5. 各种语言中的可变参数(java、python、c++、javascript)

    索引: java python c++ js 1.Java public class Animal { // 接受可变参数的方法 void eat(String... Objects) { for ( ...

  6. C函数和宏中的可变参数

    一:调用惯例 函数的调用方和被调用方对函数如何调用应该有统一的理解,否则函数就无法正确调用.比如foo(int n, int m),调用方如果认为压栈顺序是m,n,而foo认为压栈顺序是n, m,那么 ...

  7. Swift语言中为外部参数设置默认值可变参数常量参数变量参数输入输出参数

    Swift语言中为外部参数设置默认值可变参数常量参数变量参数输入输出参数 7.4.4  为外部参数设置默认值 开发者也可以对外部参数设置默认值.这时,调用的时候,也可以省略参数传递本文选自Swift1 ...

  8. C# 中的可变参数方法(VarArgs)

    首先需要明确一点:这里提到的可变参数方法,指的是具有 CallingConventions.VarArgs 调用约定的方法,而不是包含 params 参数的方法.可以通过MethodBase.Call ...

  9. 【转】C,C++中使用可变参数

    可变参数即表示参数个数可以变化,可多可少,也表示参数的类型也可以变化,可以是 int,double还可以是char*,类,结构体等等.可变参数是实现printf(),sprintf()等函数的关键之处 ...

随机推荐

  1. T4 模板

    T4模板入门 T4,即4个T开头的英文字母组合:Text Template Transformation Toolkit.T4(Text Template Transformation Toolkit ...

  2. 虚拟机上安装centos8.0

    一.准备宿主机 为了培训Hadoop生态的部署和调优技术,需要准备3台虚拟机部署Hadoop集群环境,能够保证HA,即主要服务没有单点故障,可执行基本功能,完成小内存模式的参数调整. 1.1.准备安装 ...

  3. PHP current

    1.函数的作用:返回数组的当前元素 2.函数的参数: @params array &$array 3.例子: <?php $arr = [null,'PK',false]; : ; ec ...

  4. OSI七层模型和五层TCP/IP协议

    1.查公网ip的方法: windows,打开浏览器,访问百度,搜IP即可 linux:curl ifconfig.me 2.OSI七层模型 ==网络工程师:== 物理层 1层,通信介质的信号到数字信号 ...

  5. Mongoose-modified-at 时间自动记录插件介绍

    Mongoose-modified-at 是一款自动更新字段变化时间并记录到数据库中的 Mongoose 插件,类似 Mongoose 自带的 timestamps 功能. 使用场景 让我们考虑一个场 ...

  6. jQuery插件图片懒加载lazyload

    来自XXX的前言: 什么是ImageLazyLoad技术 在页面上图片比较多的时候,打开一张页面必然引起与服务器大数据量的 交互.尤其是对于高清晰的图片,占的几M的空间.ImageLazyLoad技术 ...

  7. vue-cli2、vue-cli3脚手架详细讲解

    前言: vue脚手架指的是vue-cli它是vue官方提供的一个快速构建单页面(SPA)环境配置的工具,cli 就是(command-line-interface  ) 命令行界面 .vue-cli是 ...

  8. LeetCode686——Repeated String Match

    题目:Given two strings A and B, find the minimum number of times A has to be repeated such that B is a ...

  9. OsmocomBB软件实现栈概况

    OsmocomBB软件实现栈概况 简单地说,本文仅描述软件中GSM信号接收到部分. 暂不提及发送流程,引导加载/引导流程,以及各种控制路径特别是从layer1到RF硬件. 首先,通过天线接收RF信号, ...

  10. 变量 + 数据类型(数字 + 字符串)(day03整理)

    目录 一.上节课回顾 四 编程语言分类 (一) 机器语言 (二)汇编语言 (三) 高级语言 (四) 网络瓶颈效应 五.执行python程序两种方式 (一) 交互式(jupytre) (二) 命令行式( ...