zoukankan      html  css  js  c++  java
  • C语言中可变参数的原理——printf()函数

    函数原型: int printf(const char *format[,argument]...)

    返 回 值: 成功则返回实际输出的字符数,失败返回-1.

    函数说明:

    使用过C语言的人所再熟悉不过的printf函数原型,它的参数中就有固定参数format和可变参数(用"…"表示),format后面的参数个数不确定,且类型也不确定,这些参数都存放在栈内。而程序员又可以用各种方式来调用printf,如: 

                printf("%d ",value);   

                printf("%s ",str);   

                printf("the number is %d,string is:%s ",value,str); 

    调用printf()函数时,根据format里的格式("%d %f...")依次将栈里参数取出。而取出动作要用到va_arg、va_end、va_start这三个宏定义,再加上va_list。

         (1)va_list事实上是一char *类型,即:

                typedef char* va_list;

         (2)三个宏定义:

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

    为了字节对齐,将n的长度化为int长度的整数倍。

    补充:对((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) 的解释

    1.举个栗子解释一下内存对齐是什么?

          比方说有一个箱子可以装4个瓶子,我有8个瓶子 ,那么我需要2个箱子如果我有10个瓶

    子呢,我不能说我需要10除4,需要2.5个箱子吧。实际上我需要3个箱子,那怎么求我实际需

    要的箱子数呢?

    用一个容易理解的公式来求上述问题:

    设我的瓶子数为B,我需要的箱子数为C,一个箱子最多可以装A个瓶子。

    公式:C=(B+A-1)/A

    带入几个例子:

    B=10,  A=4   得C=13/4 ,C=3        每个箱子最多能装4个瓶子,10个瓶子需要3个箱子

    B=14,A=4  得C=17/4  ,C=4        每个箱子最多能装4个瓶子,14个瓶子需要4个箱子

    2.细致的分析一下((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1)) 

    ((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1)) :  ((sizeof(n)+sizeof(int)-1)其实就是 (B+A-1)

    a、(sizeof(n)是我们需要的实际内存大小,相对于我的瓶子数。

    b、sizeof(int)是内存大小分配的最小刻度,相对于箱子最多可以装瓶子的个数。

    c、在32位系统中,~(sizeof(int)–1) )展开为~(4-1)=~(00000011b)=11111100b,这样

    任何数 & ~(sizeof(int)–1) )后最后两位肯定为0,也就是4的整数倍。

    // e、((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1)) & ~(sizeof(int)-1))其实就是  除以A或者说除

    // 以4;((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1)) 其实就是实现栗子中的(B+A-1)/A。

    // (此处描述错误,并不是/4)

    总结:

    _INTSIZEOF(n)整个做的事情就是将n的长度化为int长度的整数倍。

    比如n为5,二进制就是101b,int长度为4,二进制为100b,那么n化为int长度的整数倍就应

    该为8。~(sizeof(int) – 1) )就应该为~(4 - 1) = ~(00000011b) = 11111100b,这样任何

    数 & ~(sizeof(int) – 1) )后最后两位肯定为0,就肯定是4的整数倍了。

    (sizeof(n) + sizeof(int) – 1)就是将大于4m但小于等于4(m + 1)的数提高到大于等于

    4(m + 1)但小于4(m + 2),这样再& ~(sizeof(int) – 1))后就正好将原长度补齐到4的倍数

    了。

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

    通过该宏定义可以获取到可变参数表的首地址,并将该地址赋给指针ap。

               

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

    通过该宏定义可以获取当前ap所指向的可变参数,并将指针ap指向下一个可变参

    数。注意,该宏的第二个参数为类型。

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

    通过该宏定义可以结束可变参数的获取。

    可以看出,该函数的参数格式不固定,参数类型不固定。在C语言中使用宏来处理这些可变参数。这些宏看起来很复杂,其实原理挺简单,即根据参数入栈的特点从最靠近第一个可变参数的固定参数开始,依次获取每个可变参数的地址。

       

    程序员通过这三个宏定义就可以实现对可变参数的处理。例如:

    1 #include <stdio.h>  
    2   
    3 typedef char* va_list;   
    4
    5 #define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))   
    6 #define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )   
    7 #define va_arg(ap,type) ( *(type *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )   
    8 #define va_end(ap)      ( ap = (va_list)0 )   
    9   
    10 int cal_val(int c, ...)   
    11 {   
    12         int sum = c;   
    13         va_list ap;              //声明指向char型的指针  
    14     va_start(ap, c);         //获取可变参数列表的首地址,并赋给指针ap  
    15   
    16     c = va_arg(ap, int);     //从可变参数列表中获取到第一个参数(返回值即为参数)  
    17     while(0 != c)   
    18     {   
    19         sum += c;   
    20         c = va_arg(ap,int);  //循环的从可变参数列表中获取到参数(返回值即为参数)  
    21     }  
    22     va_end(ap);              //结束从可变参数列表中获取参数  
    23     return sum;   
    24 }    
    25    
    26 int main(int argc, char* argv[])   
    27 {   
    28         int value1, value2; 
    29         value1 = cal_val(1,2,3,4,5,6,7,8,9,0);   
    30         printf("value1=%d/n",value1);  
    31         value2 = cal_val(6,7,8,9,0);   
    32

  • 相关阅读:
    20165214 第八周学习任务
    20165214 实验二 Java面向对象程序设计
    20165214 结队编程项目-四则运算
    20165214 第七周学习任务
    20165214 第六周学习任务
    20165214 实验一 Java开发环境的熟悉
    20165313 《Java程序设计》第八周学习总结
    结对编程-四则运算
    2017-2018-2 20165313实验二《Java面向对象程序设计》
    20165313 《Java程序设计》第七周学习总结
  • 原文地址:https://www.cnblogs.com/lilto/p/11876353.html
Copyright © 2011-2022 走看看