c语言制作函数库 c语言的库函数源码( 三 )


int __cdecl _stbuf(FILE *);
void __cdecl _ftbuf(int, FILE *);
int __cdecl _output(FILE *, const char *, va_list);
在output函数中,读取格式字符串中的每一个字符,然后对其进行处理 , 处理方式根据每一个字符所代表的意义来进行,如:普通字符直接利用函数WRITE_CHAR(ch, charsout);输出到控制台 。
其中的主要部分是对转换说明符(d,c,s,f)的处理,现在将对其中的部分代码进行详细说明 , 这里只说明最基本的转换说明符,对这些须基本的转换说明符进行修饰的修饰符,程序中单独进行处理 。下面是函数output()(output.c)部分源代码:
case ST_TYPE:
//表示当前处理的字符的类型为转换说明符 。
...
switch (ch) {
//下面对参数的获取都是利用宏va_arg( va_list arg_ptr, type );来进行的 。
case ''c'': {
//从参数表中获取单个字符,输出到缓冲字符串中,此时,type=int
buffer[0] = (char) get_int_arg(argptr); /* get char to print */
text = buffer;
textlen = 1; /* print just a single character */
}
break;
case ''s'': {
//从参数表中获取字符串,输出到缓冲字符串中 , 此时 , type=char*
int i;
char *p; /* temps */
text = get_ptr_arg(argptr);
...
}
break;
case ''w'': {
//对宽字符进行处理
...
} /* case ''w'' */
break;
...
case ''e'':
case ''f'':
case ''g'': {
//对浮点数进行操作
...
#if !LONGDOUBLE_IS_DOUBLE
/* do the conversion */
if (flagsFL_LONGDOUBLE) {
_cldcvt((LONGDOUBLE*)argptr, text, ch, precision, capexp);
va_arg(argptr, LONGDOUBLE);
//对长双精度型进行处理,此时,type=long double
}
else
#endif /* !LONGDOUBLE_IS_DOUBLE */
{
//对双精度型进行处理,此时,type=double
_cfltcvt((DOUBLE*)argptr, text, ch, precision, capexp);
va_arg(argptr, DOUBLE);
}
...
break;
//对整型变量处理
case ''d'':
case ''i'':
...
goto COMMON_INT;
case ''u'':
radix = 10;
goto COMMON_INT;
case ''p'':
...
goto COMMON_INT;
case ''o'':
...
注:对于浮点型double和long double,有相应的转换说明符(%f表示双精度型,%lf表示长双精度型) , 而float却没有 。其中的原因是,在KRC下,float值用于表达式或用作参数前,会自动转换成double类型 。而ANSI C一般不会自动把float转换成double 。有些程序已假定其中的float参数会被转换成double,为了保护大量这样的程序,所有printf()函数的float参数还是被自动转换成double型 。因此,在KRC或ANSI C下,都无需用特定的转换说明符来显示float型 。
综上所述,转换说明符必须与待打印字符的类型 。通常,用户有种选择 。例如,如要打印一个int类型的值 。则只可以使用%d,%x或%o 。所有这些说明符都表示要打印一个int类型的值;它们只不过提供了一个数值的几种不同表示 。类似一 , 可以用%f、%g和%e来表示double类型的值 。但如果转换说明与类型不匹配,将会出现意想不到的结果 。为什么呢?问题就在于C向函数传递信息的方式 。
这个失败的根本细节与具体实现相关 。它决定了系统中的参数以何方式传递 。函数调用如下:
float n1;
double n2;
long n3;
long n4;
...
printf("%ld,%ld,%ld,%ld",n1,n2,n3,n4);
这个调用告诉计算机,要把变量n1,n2,n3和n4的值交给计算机,它把这些变量放进称作栈(stack)的内存区域中,来完成这一任务 。计算机把这些值放进栈中 , 其根据是变量的类型而不是转换说明符 , 比如n1,把8个字节放入栈中(float被转换成double),类似地,为n2放了8字节,其后给n3和n4各放了4个字节 。接着 , 控制的对象转移到printf();此函数从栈中读数 , 不过在这一过程中,它是在转换说明符的指导下,读取数值的 。说明符%ld指定printf()应读4个字节(va_arg( va_list arg_ptr, type )中type=long) , 因此printf()读入栈中的4个字节,作为它的第一个值 。但是这只是n1的前半部分,这个值被看成一个long整数 。下一个说明符%ld读入4个字节,这正是n1的后半部分 , 这个值被看成第二个long整数 。类似地,第三、第四次又读入n2的前后两部分 。因此,尽管我们对n3和n4使用了正确的说明符,printf()仍然会产生错误 。

推荐阅读