研究printf的实现,首先来看看printf函数的函数体
int printf(const char *fmt, ...)
{
int i;
char buf[256];
   
     va_list arg = (va_list)((char*)(&fmt) + 4);
     i = vsprintf(buf, fmt, arg);
     write(buf, i);
   
     return i;
    }
    代码位置:D:/~/funny/kernel/printf.c
   
    在形参列表里有这么一个token:...
    这个是可变形参的一种写法。
    当传递参数的个数不确定时,就可以用这种方式来表示。
    很显然,我们需要一种方法,来让函数体可以知道具体调用时参数的个数。
   
    先来看printf函数的内容:
   
    这句:
   
    va_list arg = (va_list)((char*)(&fmt) + 4);
   
    va_list的定义:
    typedef char *va_list
    这说明它是一个字符指针。
    其中的: (char*)(&fmt) + 4) 表示的是...中的第一个参数。
    如果不懂,我再慢慢的解释:
    C语言中,参数压栈的方向是从右往左。
    也就是说,当调用printf函数的适合,先是最右边的参数入栈。
    fmt是一个指针,这个指针指向第一个const参数(const char *fmt)中的第一个元素。
    fmt也是个变量,它的位置,是在栈上分配的,它也有地址。
    对于一个char *类型的变量,它入栈的是指针,而不是这个char *型变量。
    换句话说:
    你sizeof(p) (p是一个指针,假设p=&i,i为任何类型的变量都可以)
    得到的都是一个固定的值。(我的计算机中都是得到的4)
    当然,我还要补充的一点是:栈是从高地址向低地址方向增长的。
    ok!
    现在我想你该明白了:为什么说(char*)(&fmt) + 4) 表示的是...中的第一个参数的地址。
   
    下面我们来看看下一句:
     i = vsprintf(buf, fmt, arg);
   
    让我们来看看vsprintf(buf, fmt, arg)是什么函数。 
    
   

char *fmt, va_list args)
   { 
    char* p; 
    char tmp[256]; 
    va_list p_next_arg = args; 
   
    for (p=buf;*fmt;fmt++) { 
    if (*fmt != '%') { 
    *p++ = *fmt; 
    continue; 
    } 
   
    fmt++; 
   
    switch (*fmt) { 
    case 'x': 
    itoa(tmp, *((int*)p_next_arg)); 
    strcpy(p, tmp); 
    p_next_arg += 4; 
    p += strlen(tmp); 
    break; 
    case 's': 
    break; 
    default: 
    break; 
    } 
    } 
   
    return (p - buf); 
   } 

相关文章:

  • 2022-12-23
  • 2021-10-02
  • 2022-12-23
  • 2021-08-14
  • 2021-09-29
  • 2022-12-23
  • 2023-03-15
猜你喜欢
  • 2022-12-23
  • 2021-11-28
  • 2022-01-17
  • 2022-01-01
  • 2022-12-23
  • 2022-12-23
  • 2021-08-28
相关资源
相似解决方案