zoukankan      html  css  js  c++  java
  • C语言函数可变长度参数剖析

    C语言中的很多函数的入参被定义为可变参数,最典型的

    int printf (const char * fmt, ...)

    要对其中的可变参数进行处理,就要用到va_list类型和 VA_START, VA_END, VA_ARG 宏 ,需要包含<stdarg.h>头文件

    利用va族函数对不定参数进行解析的过程所示如下:

     1 int my_printf(const char * fmt, ...)
     2 {
     3     va_list struAp;
     4     va_start(struAp, fmt);
     5 
     6     for (; *fmt; ++fmt)
     7     {
     8         if(*fmt != '%')
     9         {
    10             PUTC(*fmt); 
    11             continue;
    12         }
    13         
    14         fmt++;
    15         
    16         switch (*fmt)
    17         {
    18             case 'd':
    19             {
    20                 int i = va_arg(struAp,int);
    21                 PUTC(i);
    22             }
    23             break;
    24 
    25             default:
    26                 break;
    27         }
    28     }
    29     
    30     va_end(struAp);
    31 }

    要了解不定参数的处理方式,就要搞清楚va族函数的实现

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

    /* 实质就是一个char型的指针 */

    typedef char * va_list;

    /* 将指针偏移一个v的长度,指向后面的地址 */

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

    /* 根据参数类型,取出后面的数据,强制类型转换 */

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

    /* 将指针置为NULL */

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

    通过解析fmt字符串,得到后面的参数类型和个数,根据参数类型再加上偏移量就可以找到栈中的不定参数了

     函数调用和传参的过程所示如下:

    将函数参数与函数调用后下一条指令的地址都压入栈中,然后跳到函数的入口地址。

    例如

    void func(int param1, double param2,int param3){ }
    
    int main()
    { 
        func(3, 1.2, 4); 
        printf("Over
    "); //设指令地址为0x1234
        return 0;
    }

    执行f(3, 1.2, 4)的函数调用,进入func函数时的堆栈如下:

     
     这样,通过param1的地址就可以计算出param2与param3的地址:
     
    使用不定参函数的注意事项
     
    • 在C语言中,调用一个可变参数函数时,调用者会对每个参数执行“默认实际参数提升(default argument promotions)”

         ——float类型的实际参数将提升到double
      ——char类型的实际参数将提升到int
      ——short类型的实际参数将提升到int

    • 在没有函数原型的情况下,char与short类型都将被转换为int类型,float类型将被转换为double类型。

                 ——《C语言程序设计》第2版  2.7 类型转换 p36

    • 这样写肯定是不对的:

        c = va_arg(ap,char);

      因为我们无法传递一个char类型参数,如果传递了,它将会被自动转化为int类型。上面的式子应该写成:
      c = va_arg(ap,int);

                    ——《C陷阱与缺陷》p164
  • 相关阅读:
    宿主机( win 7 系统) ping 虚拟机VMware( cent os 6.6 ) 出现“请求超时”或者“无法访问目标主机”的解决方法
    Java实现 LeetCode 23 合并K个排序链表
    Java实现 LeetCode 23 合并K个排序链表
    Java实现 LeetCode 23 合并K个排序链表
    Java实现 LeetCode 22 括号生成
    Java实现 LeetCode 22 括号生成
    Java实现 LeetCode 22 括号生成
    Java实现 LeetCode 21 合并两个有序链表
    Java实现 LeetCode 21 合并两个有序链表
    Java实现 LeetCode 21 合并两个有序链表
  • 原文地址:https://www.cnblogs.com/li-hao/p/3401942.html
Copyright © 2011-2022 走看看