/***
*printf.c - print formatted
*
*Purpose:
* defines printf() - print formatted data
*******************************************************************************/
#include
*int printf(format, ...) - print formatted data
* Prints formatted data on stdout using the format string to
* format data and getting as many arguments as called for
* Uses temporary buffering to improve efficiency.
* _output does the real work here
*Entry:
* char *format - format string to control data format/number of arguments
* followed by list of arguments, number and type controlled by
* format string
*Exit:
* returns number of characters printed
*Exceptions:
int __cdecl printf (
const char *format,
...
)
/*
* stdout ''PRINT'', ''F''ormatted
*/
{
va_list arglist;
int buffing;
int retval;
va_start(arglist, format);
_ASSERTE(format != NULL);//断言宏.如果输出格式字符串指针为空,则在DEBUG版下断言,报告错误.
buffing = _stbuf(stdout);//stdout:指定输出到屏幕
retval = _output(stdout,format,arglist);
_ftbuf(buffing, stdout);
return(retval);
}
以上为printf()的源代码
①.、从含有可选参数函数中获得可选参数,以及操作这些参数
typedef char *va_list;
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
假定函数含有一个必选参数和多个可选参数,必选参数声明为普通数据类型,且能通过参数名来获得该变量的值.可选参数通过宏va_start、va_arg和va_end(定义在stdarg.h或varargs.h中)来进行操作,即通过设置指向第一个可选参数指针、返回当前参数、在返回参数后重新设置指针来操作所有的可选参数.
va_start:为获取可变数目参数的函数的参数提供一种便捷手段.设置arg_ptr为指向传给函数参数列表中的第一个可选参数的指针,且该参数必须是va_list类型.prev_param是在参数列表中第一个可选参数前的必选参数.
va_arg:返回由arg_ptr所指向的参数的值,且自增指向下一个参数的地址.type为当前参数的类型,用来计算该参数的长度,确定下一个参数的起始位置.它可以在函数中应用多次,直到得到函数的所有参数为止,但必须在宏va_start后面调用.
va_end:在获取所有的参数后,设置指针arg_ptr为NULL.
下面举例说明:
int average( int first, ... );
void main( void )
/* Call with just -1 terminator. */
printf( "Average is: %d\n", average( -1 ) );
int average( int first, ... )
int count = 0, sum = 0, i = first;
va_list marker;
va_start( marker, first ); /* Initialize variable arguments. */
while( i != -1 )
sum += i;
count++;
i = va_arg( marker, int);
va_end( marker ); /* Reset variable arguments. */
return( sum ? (sum / count) : 0 );
返回值为:
Average is: 0
综合上面所讲的,在printf()函数中,可以只输出一个字符串,也可按照一定的形式输出含有多个可选参数的字符串信息.所以呢,首先就要通过这些宏来获取所有的可选参数.在上面的源码可以看出printf()中,只使用了宏at_start,将可选参数的首地址赋给了arglist.
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);
case ''w'': {
//对宽字符进行处理
} /* case ''w'' */
case ''e'':
case ''f'':
case ''g'': {
//对浮点数进行操作
#if !LONGDOUBLE_IS_DOUBLE
/* do the conversion */
if (flags FL_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);
//对整型变量处理
case ''d'':
case ''i'':
goto COMMON_INT;
case ''u'':
radix = 10;
case ''p'':
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;
int
getdata(int
a,int
b)//括号是形式参数,前面int
是函数返回值类型
return
a;//返回值
...是实现的代码.
自己看看书上的函数定义和实现形式很容易的.
在C语言中,一个标准的函数定义语句块必须包含函数返回值的类型标识符、函数名、形参类型及数量、函数体、返回值表达式.如果函数返回值类型为 void (即无返回值),则在两个大括号之间不能写带有返回值表达式的 return 语句,否则编译器就会报错.你写的 test 函数返回值类型为 void,而你在函数定义语句块内写下了 return 语句,编译器自然会报错了.
你可以将这个函数修改为以下形式:
int test(int n)
return m;
(1)素数判断函数:是返回1,否则返回0
int?prime(int?n){
int?i;
return?0;
if(!(n%i))
return?1;
void?bubbling(int?*p,int?n){//冒泡
int?i,j,k;
for(i=0;in;i++)
for(j=1;jn;j++)
if(p[j]p[j-1])
k=p[j],p[j]=p[j-1],p[j-1]=k;
void?select(int?*p,int?n){//选择
for(n--,i=0;in;i++){
for(k=i,j=k+1;j=n;j++)
if(p[k]p[j])
k=j;
if(k-i)
j=p[k],p[k]=p[i],p[i]=j;
int?find(int?*p,int?n,int?x){//顺序
if(p[i]==x)
return?i;
return?-1;
int?fihalf(int?*p,int?n,int?x){//折半,升序为例
int?l,r,m;
l=0,r=n-1;
while(m=(l+r)1,lr?p[m]-x)
p[m]x?l=m+1?:?r=m-1;
return?p[m]==x?m?:?-1;