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)所代表的长度来取值,而不在乎其后b,c的值是多少,只要有这个参数就行。
为了验证以上想法,我们再来看
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取从0xbffa88c4到0xbffa88c8的内容,并且本机器是小尾端体系结构,即低地址存低字节,高低值存高字节,比如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);//结束时的清理工作}
如有错误,还望指出,谢谢!
- pirntf()函数心得(笔试后的忏悔)
- 2006 我的忏悔.
- 老公的忏悔
- 老公的忏悔
- 初学者的忏悔!- -,
- “周九耕”的忏悔书
- 老公的忏悔
- 11.15的忏悔
- 面向对象的忏悔
- 老师的忏悔
- 天堂的忏悔
- 老公的忏悔
- 忏悔(续一)
- 忏悔
- 忏悔
- 忏悔
- 忏悔
- 忏悔
- c#操作excel方式四-Aspose控件
- Unity3D属性监视面板(Inspector)
- 10g OCM考试大纲 (中文版)
- Android的Intent的深度剖析
- C#实现MD5加密
- pirntf()函数心得(笔试后的忏悔)
- Cannot assign requested address
- 应用系统模块化设计文档结构
- bazaar and launchpad
- android 自定义 radiobutton 文字颜色随选中状态而改变
- char * a, char ** a, char * a[], char a[][], char * a[][], char ** a[][], char * a [][][],
- XMLHttpRequest对象连接池
- 系统架构师谈企业应用架构之数据访问层
- Android 设置 横屏 竖屏