pirntf()函数心得(笔试后的忏悔)

来源:互联网 发布:儋州网络在逃人员 编辑:程序博客网 时间:2024/06/07 01:58

  笔试题

  printf()函数原型:intprintf(const char*format,[argument]);基本用法就不在赘述了,也不讲一步步实现细节,估计很多人都会看不下去,这些网上都有。接下来主要讲的是如何取数打印出来。

首先,来看一个例子(某知名IT公司的笔试题):

test1.c

#include<stdio.h>

intmain()

{

long long a=1;

long long b=2;

long long c=3;

printf("a=%d\nb=%d\nc=%d\n",a,b,c);

}

上面的程序结果是什么?运行后可以看到,结果是:

a=1

b=0

c=2

看到上面的运行结果,很多人第一眼会很诧异,这里我讲讲我的想法,不知道真实情况是不是我想的这样,仅供参考,不对之处,还望指出。

首先先看内存的中的分布情况。

test2.c

#include<iostream>

#include<stdio.h>

usingnamespace std;

intmain()

{

longlong a=1;

longlong b=2;

longlong c=3;

signedint *m;

m= (int *)&a;

for(int i=0; i < 6; i++)

{

if(i % 2 == 0)

printf("\n");

printf("%p=%d ",m+i,*(m+i));

}

printf("\n");

printf("&a=%p\n",&a);

printf("&b=%p\n",&b);

printf("&c=%p\n",&c);

printf("\n");

printf("a=%d,b=%d,c=%d\n",a,b,c);

printf("a=%lld,b=%d,c=%d\n",a,b,c);

printf("a=%d,b=%lld,c=%d\n",a,b,c);

printf("a=%d,b=%lld,c=%lld\n",a,b,c);

printf("\n\n\n");

longlong n = 8589934592;

int*Ln;

Ln= (int *)&n;

printf("%lld的高字节内容为:%d\n",n,*(Ln+1));

printf("%lld的低字节内容为:%d\n",n,*Ln);

return0;

}

运行结果:


由上面的程序运行结果可知,数据在内存中的分布如下:

地址

存的内容

0xbffa88c0

1

0xbffa88c4

0

0xbffa88c8

2

0xbffa88cc

0

0xbffa88d0

3

0xbffa88d4

0

       printf("a=%d,b=%d,c=%d\n",a,b,c);取的是从地址a开始的前三个字节的内容。首先a=%d,从a开始取一个字节,因为用了”%d”,所以输出1,随后b=%d,是紧跟这上次的地址(0xbffa88c4)后取一个字节,而不是从0xbffa88c8开始取的,我想可能printf只获取第二个参数(本例子中的a)的地址,然后根据格式(%d)所代表的长度来取值,而不在乎其后bc的值是多少,只要有这个参数就行。

      为了验证以上想法,我们再来看

      printf("a=%lld,b=%d,c=%d\n",a,b,c);很容易知道符合以上想法的。

      printf("a=%d,b=%lld,c=%d\n",a,b,c);首先a取了第一个字节即1输出,而后b取从0xbffa88c40xbffa88c8的内容,并且本机器是小尾端体系结构,即低地址存低字节,高低值存高字节,比如inta = 0x1234;低地址存的内容是0x34,高字节存的内容是0x12。所以输出结果是8589934592

     printf("a=%d,b=%lld,c=%lld\n",a,b,c);的输出结果同理分析。


printf()实现原理(类源代码):

     后来查阅了一下书籍,是这么介绍printf函数的。

     函数printf的正确声明形式为:

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

     其中,省略号表示参数表中参数的数量和类型是可变的。省略号只能出现在参数表的尾部。printf的关键在于如何处理一个甚至连名字都没有的参数表。标准头文件<stdarg.h>中包含一组宏定义,它们堆如何遍历参数表进行了定义。

     va_list类型用于声明一个变量,该变量一次引用各参数。我们自己编写一个printf的简化版函数myprintf。在函数myprintf中,我们将定义一个va_list变量ap,意思是“参数指针”。宏va_start将ap初始化为第一个无名参数的指针。在使用ap之前,该宏必须被调用一次。参数表必须至少包括一个有名参数,va_start将最后一个有名参数最为起点。

     每次调用va_arg,该函数都将返回一个参数,并将ap指向下一个参数。va_arg使用一个类型名来决定返回的对象类型、指针移动的步长。最后,必须在函数返回之前调用va_end,以完成一些必要的清理工作。

     基于上面的讨论,我们实现的简化printf函数如下所示:

    

#include <stdarg.h>/* myprintf函数:带有可变参数列表的简化的printf函数*/void myprintf( char *fmt, ... ){    va_list ap; //依次指向每个无名参数    char *p, *sval;    int ival;    double dval;    va_start(ap,fmt);//将ap指向第一个无名参数    for ( p = fmt; *p; p++ )    {if ( *p != '%' ){    putchar(*p);    continue;}switch ( *++p ){    case 'd':ival = va_arg(ap,int);printf("%d",ival);break;    case 'f':dval = va_arg(ap,double);printf("%f",dval);break;    case 's':for ( sval = va_arg(ap,char *); *sval; sval++ )    putchar(*sval);break;    default:putchar(*p);break;}    }    va_end(ap);//结束时的清理工作}


     如有错误,还望指出,谢谢!

原创粉丝点击