OneWayOrAnother 2015-05-30 15:44 采纳率: 30%
浏览 1870
已采纳

C 关于printf() 我真的很想问的一个问题!!!!

编译器怎样检查 printf () 参数个数的?
刚刚学C 不久,不知道怎么找,于是到这里提问来了。。。
提前谢谢所有回答的人!!! thanks for your time .

  • 写回答

4条回答 默认 最新

  • WorldMobile 2015-05-30 15:48
    关注

    参考一下这个回答,对你应该有帮助

    C中的可变参数研究

    一. 何谓可变参数
    int printf( const char* format, ...);
    这是使用过C语言的人所再熟悉不过的printf函数原型,它的参数中就有固定参数format和可变参数(用”…”表示). 而我们又可以用各种方式来调用printf,如:
    printf("%d",value);
    printf("%s",str);
    printf("the number is %d ,string is:%s", value, str);

    二.实现原理
    C语言用宏来处理这些可变参数。这些宏看起来很复杂,其实原理挺简单,就是根据参数入栈的特点从最靠近第一个可变参数的固定参数开始,依次获取每个可变参数的地址。下面我们来分析这些宏。在VC中的stdarg.h头文件中,针对不同平台有不同的宏定义,我们选取X86平台下的宏定义:

    typedef char va_list;
    /
    把va_list被定义成char*,这是因为在我们目前所用的PC机上,字符指针类型可以用来存储内存单元地址。而在有的机器上va_list是被定义成void*的*/
    #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
    /*_INTSIZEOF(n)宏是为了考虑那些内存地址需要对齐的系统,从宏的名字来应该是跟sizeof(int)对齐。一般的sizeof(int)=4,也就是参数在内存中的地址都为4的倍数。比如,如果sizeof(n)在1-4之间,那么_INTSIZEOF(n)=4;如果sizeof(n)在5-8之间,那么_INTSIZEOF(n)=8。*/
    #define va_start(ap,v)( ap = (va_list)&v + _INTSIZEOF(v) )
    /*va_start的定义为 &v+_INTSIZEOF(v) ,这里&v是最后一个固定参数的起始地址,再加上其实际占用大小后,就得到了第一个可变参数的起始内存地址。所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在的内存地址*/
    #define va_arg(ap,t) ( (t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
    /
    这个宏做了两个事情,
    ①用用户输入的类型名对参数地址进行强制类型转换,得到用户所需要的值
    ②计算出本参数的实际大小,将指针调到本参数的结尾,也就是下一个参数的首地址,以便后续处理。*/
      #define va_end(ap) ( ap = (va_list)0 )
    /*x86平台定义为ap=(char*)0;使ap不再 指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的. 在这里大家要注意一个问题:由于参数的地址用于va_start宏,所以参数不能声明为寄存器变量或作为函数或数组类型. */

    以下再用图来表示:

    在VC等绝大多数C编译器中,默认情况下,参数进栈的顺序是由右向左的,因此,参数进栈以后的内存模型如下图所示:最后一个固定参数的地址位于第一个可变参数之下,并且是连续存储的。
    |——————————————————————————|
    |最后一个可变参数 | ->高内存地址处
    |——————————————————————————|
    ...................
    |——————————————————————————|
    |第N个可变参数 | ->va_arg(arg_ptr,int)后arg_ptr所指的地方,
    | | 即第N个可变参数的地址。
    |——————————————— |
    ………………………….
    |——————————————————————————|
    |第一个可变参数 | ->va_start(arg_ptr,start)后arg_ptr所指的地方
    | | 即第一个可变参数的地址
    |——————————————— |
    |———————————————————————— ——|
    | |
    |最后一个固定参数 | -> start的起始地址
    |—————————————— —| .................
    |—————————————————————————— |
    | |
    |——————————————— |-> 低内存地址处

    三.printf研究

    下面是一个简单的printf函数的实现,参考了中的156页的例子,读者可以结合书上的代码与本文参照。
    #include "stdio.h"
    #include "stdlib.h"
    void myprintf(char* fmt, ...) //一个简单的类似于printf的实现,//参数必须都是int 类型
    {
    char* pArg=NULL; //等价于原来的va_list
    char c;

    pArg = (char*) &fmt; //注意不要写成p = fmt !!因为这里要对//参数取址,而不是取值
    pArg += sizeof(fmt); //等价于原来的va_start

    do
    {
    c =*fmt;
    if (c != '%')
    {
    putchar(c); //照原样输出字符
    }
    else
    {
    //按格式字符输出数据
    switch(*++fmt)
    {
    case 'd':
    printf("%d",*((int*)pArg));
    break;
    case 'x':
    printf("%#x",*((int*)pArg));
    break;
    default:
    break;
    }
    pArg += sizeof(int); //等价于原来的va_arg
    }
    ++fmt;
    }while (*fmt != '\0');
    pArg = NULL; //等价于va_end
    return;
    }

    int main(int argc, char* argv[])
    {
    int i = 1234;
    int j = 5678;

    myprintf("the first test:i=%d",i,j);
    myprintf("the secend test:i=%d; %x;j=%d;",i,0xabcd,j);
    system("pause");
    return 0;
    }

    在intel+win2k+vc6的机器执行结果如下:
    the first test:i=1234
    the secend test:i=1234; 0xabcd;j=5678;

    四.应用
    求最大值:
    #include //不定数目参数需要的宏
    int max(int n,int num,...)
    {
    va_list x;//说明变量x
    va_start(x,num);//x被初始化为指向num后的第一个参数
    int m=num;
    for(int i=1;i {
    //将变量x所指向的int类型的值赋给y,同时使x指向下一个参数
    int y=va_arg(x,int);
    if(y>m)m=y;
    }
    va_end(x);//清除变量x
    return m;
    }

    int main()
    {
    printf("%d,%d",max(3,5,56),max(6,0,4,32,45,533));
    return 0;
    }

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

悬赏问题

  • ¥15 #MATLAB仿真#车辆换道路径规划
  • ¥15 java 操作 elasticsearch 8.1 实现 索引的重建
  • ¥15 数据可视化Python
  • ¥15 要给毕业设计添加扫码登录的功能!!有偿
  • ¥15 kafka 分区副本增加会导致消息丢失或者不可用吗?
  • ¥15 微信公众号自制会员卡没有收款渠道啊
  • ¥100 Jenkins自动化部署—悬赏100元
  • ¥15 关于#python#的问题:求帮写python代码
  • ¥20 MATLAB画图图形出现上下震荡的线条
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘