谁知道printf()是如何实现的?有源码吗?
谁知道printf()是如何实现的?有源码吗? 问题点数:100、回复次数:22Top
1 楼Caoyu015(酷鱼一代)回复于 2003-02-03 01:47:32 得分 45
很久以前研究过,现在记得不是很清楚了。
printf函数的实现比较的复杂,但是大体上是这样的。
函数用到两个比较特别的类型称之为 va_list 它主要用来访问调用函数时的参数,<其实说穿了也就是通过访问栈来实现的>
由于printf函数的参数是可变数目的,所以首先通过va_list变量来访问第一的参数也就是一个可以肯定的参数-字符串指针。
大概的实现我随便写写,不一定对,但是思路就是这样了。
int printf( const char *szFormat,... //声明可变数目的参数 )
{
va_list vaOldStack;
va_start( vaOldStack ); //初始化变量
const char *szAnalysis = va_arg( vaOldStack, char * );
//存取了第一个参数,vaOldStack被更新指向下一个参数的位置
// 开始分析字符串
while( szAnalysis[ i ] != '\0' )
{
if( szAnalysis[ i ] == '%' ) // 判定格式
switch( szAnalysis[ i + 1 ] )
{
case 'c': //是要求输出字符
int ch = va_arg( vaOldStack, char ) //从栈中取两个字节,
vaOldStack被更新,指向下一个参数地址。
putch( ch );
++i;
break;
case 'd': //是要求输出整数
int Value = va_arg( vaOldStack, int )// 同样取两个字节
// 处理整数输出...
++i;
break;
case 'f': //
......................
以上只考虑到一小部分情况,实际还有可能要处理格式字符如
"%6d" 这样的。这些函数都要自己编写处理,有些比较复杂,有些简单 如处理字符输出。
default:
putch(szAnalysis[ i ] );
}
++i;
}
va_end( va_list ); //加上这句才行,因为va_start()有可能自己分配了内存需要释放。
}
Top
2 楼Caoyu015(酷鱼一代)回复于 2003-02-03 01:52:35 得分 0
如果真是那么有兴趣的话可以参考一下 Andrew Koenig的著作《 C的陷阱与
缺陷》上面有很详细的讨论。 我得这些东东是以前读书的时候自己研究的,
不知道对不对。
Top
3 楼myc()回复于 2003-02-03 03:00:32 得分 0
TO: Caoyu015()
佩服佩服。能说说你是如何研究的吗?还有,如何深入点学C。
很荣幸能遇到你。Top
4 楼acange(acan)回复于 2003-02-03 04:33:19 得分 10
MiniUnix/usr/source/iolib/printf.c
char *_ptrbf, *_ptrst, *__fmt;
printf(a1,a2,a3,a4){
auto char c, *s, adj, *ptr,*p, buf[30];
extern cputc(),_putstr(), cout;
auto int *adx, x, n, m, width, prec,i, padchar, fd;
double zz, *dblptr;
char (*f)();
_ptrbf = buf;
fd=cout;
adx = &a1;
f = cputc;
if (a1 == -1)
{
f = _putstr;
_ptrst = a2;
adx =+ 2;
}
else if (a1 >= 0 && a1 <= 9)
fd = *adx++;
__fmt = *adx++;
while( c = *__fmt++ ){
if(c != '%') (*f)(c,fd);
else { x = *adx++;
if( *__fmt == '-' ){ adj = 'l'; __fmt++; }
else adj = 'r';
padchar = (*__fmt=='0') ? '0' : ' ';
width = __conv();
if( *__fmt == '.'){++__fmt; prec = __conv();}
else prec = 0;
s = 0;
switch ( c = *__fmt++ ) {
case 'D':
case 'd':
_prt1(x); break;
case 'o':
case 'O':
_prnt8(x); break;
case 'x':
case 'X':
_prntx(x); break;
case 'S':
case 's': s=x;
break;
case 'C':
case 'c': *_ptrbf++ = x&0777;
break;
case 'E':
case 'e':
case 'F':
case 'f':
dblptr = adx-1;
zz = *dblptr;
adx =+ 3;
ftoa (zz, buf, prec, c);
prec = 0;
s = buf;
break;
default: (*f)(c,fd);
adx--;
}
if (s == 0)
{*_ptrbf = '\0'; s = buf;}
n = _clenf (s);
n = (prec<n && prec != 0) ? prec : n;
m = width-n;
if (adj == 'r') while (m-- > 0) (*f)(padchar,fd);
while (n--) (*f)(*s++,fd);
while (m-- > 0) (*f)(padchar,fd);
_ptrbf = buf;
}
}
if(a1 == -1) (*f)('\0',fd);
}
_prnt8 (n)
{ /* print in octal */
int p, k, sw;
if (n==0) {*_ptrbf++ = '0'; return;}
sw = 0;
for (p=15; p >= 0; p =- 3)
if ((k = (n>>p)&07) || sw)
{
*_ptrbf++ = '0' + k;
sw = 1;
}
}
_prntx (n)
{
int d,a;
if (a = n>>4)
_prntx ( a & 07777);
d = n&017;
*_ptrbf++ = d > 9 ? 'A'+d-10 : '0' + d;
}
__conv()
{
auto c,n;
n = 0;
while( ((c = *__fmt++) >= '0') && (c<='9')) n = n*10+c-'0';
__fmt--;
return(n);
}
_putstr(chr,str){
*_ptrst++ = chr;
return; ieh305i(); /* force loading of dummy.s */
}
_prt1(n)
{
int digs[15], *dpt;
dpt = digs;
if (n >= 0)
n = -n;
else
*_ptrbf++ = '-';
for (; n != 0; n = n/10)
*dpt++ = n%10;
if (dpt == digs)
*dpt++ = 0;
while (dpt != digs)
{ --dpt;
*_ptrbf++ = '0' - *dpt;
}
}
Top
5 楼oustar(欧文)回复于 2003-02-03 09:20:10 得分 0
在《THE C PROGRAMMING LANGUAGE》中有一般性的讨论。
GOOD LUCK!Top
6 楼huanshilang(幻十郎)回复于 2003-02-03 13:20:53 得分 0
不错 学习一下 多谢了Top
7 楼whyNotHere(何日是尽头)回复于 2003-02-03 13:35:22 得分 0
学习中。。。Top
8 楼Caoyu015(酷鱼一代)回复于 2003-02-03 14:27:03 得分 0
大家新年好,羊年发洋财!
我个人的意见是,首先能避开C语言的陷阱写出高效CODE,这要依靠大量的上机写程序和分析原代码来作到。
C最强的是指针,所以C的灵魂也是指针。善于驾驭指针,你的C也就成功了一般了。《能懂汇编,结合汇编那将事半功倍》:)
接下来就要深入的就是操作系统和接口技术。 打个比喻,如果将C比喻成一根
如意禁箍棒, 那么这些操作系统和硬件接口技术的知识等等就是棒法. 光有好的棒子不行,还要有好的棒法. 而且你的棒法掌握的越广越精,你的如意禁锢棒也就用的越好.
以上只是我个人的妄见,听听也就算了. ^_^
Top
9 楼cupidvenus(小鱼儿)回复于 2003-02-03 14:50:08 得分 45
#include <stdio.h>
#include <_printf.h>
#include <_stdio.h>
int cdecl printf(const char *fmt,...)
{
return __vprinter((putnF *)__fputn,stdout,fmt,_va_ptr);
}
/* 而__vprinter是一个长16页的函数,需要的话给我你的邮箱 */Top
10 楼wonita(我你他)回复于 2003-02-03 15:14:09 得分 0
upTop
11 楼myc()回复于 2003-02-03 16:42:29 得分 0
TO:cupidvenus(小鱼儿)
谢谢。我想要__vprinter的源码,我的邮箱是my.c@163.com.麻烦你了。:)
TO:Caoyu015()
谢谢。能经常向你学习吗?Top
12 楼Caoyu015(酷鱼一代)回复于 2003-02-03 17:24:24 得分 0
客气!多上上csdn,上面的比我厉害的高手遍地都是。Top
13 楼swtju94_2(program)回复于 2003-02-03 18:53:23 得分 0
gzTop
14 楼myc()回复于 2003-02-03 20:38:57 得分 0
我觉得写程序,发现错误很有意义。错误能引发思考。由溢出引发的怪现象使我想知道printf()是如何实现的。printf()里面好象没有错误机制。Top
15 楼paulxj(失而复得)回复于 2003-02-03 22:02:49 得分 0
好好学习ing!!!Top
16 楼giantzz(平庸无能的政府)回复于 2003-02-03 22:48:37 得分 0
反汇编,它本身好像就是用汇编写的,反汇编应该就是看其源码
连main()的源码也能看!我现在就有,你要吗Top
17 楼myc()回复于 2003-02-03 23:07:12 得分 0
TO:giantzz(牛!是怎么死的。。。)
要啊。谢谢。my.c@163.com.Top
18 楼Caoyu015(酷鱼一代)回复于 2003-02-04 04:45:10 得分 0
在Turbo c 2.0中看反汇编的文件的方法是:
进入到tc 目录下 键入 tcc -S 文件所在目录\SourceFileName.c 既可得到 SourceFileName.asm 的汇编文件。
Turbo c 3.0中的方法一样,bcb5.5 中的方法是 bcc32 -S -I包含文件目录
-L库文件目录 SourceFileName.cpp
Top
19 楼About2Rain(山雨欲来风满楼)回复于 2003-02-04 23:32:02 得分 0
莱昂氏那本书里有Unix系统里的printf()的实现
基本就差不多了Top
20 楼cupidvenus(小鱼儿)回复于 2003-02-14 20:59:14 得分 0
已发给你了。Top
21 楼koeinova2002()回复于 2003-02-14 22:53:42 得分 0
能也给我一份吗?
koeinova@yahoo.com.cn
谢谢Top
22 楼caosheng(草圣)回复于 2003-02-20 17:30:14 得分 0
学习Top




