自己写的printf()和scanf()函数

合集下载

scanf用法

scanf用法

scanf()函数是所有C语言学习者在学习C语言过程中所遇到的第二个函数(第一个函数是printf(),Brian W.Kerninghan & Dennis M.Ritchie的“hello,world”程序基本上是所有的C语言学习者第一个范例),所以scanf()函数应当是C学习者能熟练运用的一个函数,但有很多初学者对此函数不能很好的运用,在实际编程中错误使用scanf()函数,导至程序产生某种错误不能正常运行,以至产生“scanf()函数有BUG”,“scanf()函数无用论”等等错误观点。

本文结合笔者在编程实践中及论坛上网友所遇到的问题作一释疑,但笔者水平有限(菜鸟级),难免有谬误之处,还望达人指点一二。

(Email:knocker.k@) 本文分上,下两篇讲述了C语言中的scanf()函数的用法,重点阐述使用scanf()函数过程中出现的常见错误及对策。

当然,文中某些解决方法,均可以采用其他函数和方法来更好地解决,但本文仅限讨论scanf()函数本身。

上篇,详细介绍了scanf()函数控制串的构成。

下篇,用实际例程介绍scanf()函数控制串运用出现的常见错误及对策技巧。

二、scanf()函数的控制串函数名: scanf功能: 执行格式化输入用法: int scanf(char *format[,argument,...]);scanf()函数是通用终端格式化输入函数,它从标准输入设备(键盘) 读取输入的信息。

可以读入任何固有类型的数据并自动把数值变换成适当的机内格式。

其调用格式为: scanf("<格式化字符串>",<地址表>);scanf()函数返回成功赋值的数据项数,出错时则返回EOF。

其控制串由三类字符构成:1。

格式化说明符;2。

空白符;3。

非空白符;(A)格式化说明符格式字符说明%a 读入一个浮点值(仅C99有效)%A 同上%c 读入一个字符%d 读入十进制整数%i 读入十进制,八进制,十六进制整数%o 读入八进制整数%x 读入十六进制整数%X 同上%c 读入一个字符%s 读入一个字符串%f 读入一个浮点数%F 同上%e 同上%E 同上%g 同上%G 同上%p 读入一个指针%u 读入一个无符号十进制整数%n 至此已读入值的等价字符数%[] 扫描字符集合%% 读%符号附加格式说明字符表修饰符说明L/l 长度修饰符输入"长"数据h 长度修饰符输入"短"数据W 整型常数指定输入数据所占宽度* 星号空读一个数据hh,ll同上h,l但仅对C99有效。

c语言函数名前后双下划线

c语言函数名前后双下划线

c语言函数名前后双下划线__C语言函数名前后双下划线__C语言是一种广泛应用于软件开发的编程语言,它提供了丰富的函数库以及灵活的语法结构,使得开发者能够快速高效地编写出高质量的程序。

在C语言中,函数名前后双下划线是一种常见的命名规范,用于区分系统函数和用户自定义函数,以避免冲突和命名混乱。

在C语言中,函数是一段完成特定任务的程序代码,它接受输入参数并返回输出结果。

函数名前后双下划线的命名规范,可以使函数名更加明确和易于理解,提高代码的可读性和可维护性。

下面将介绍几个常见的带有双下划线的函数名,并解释其用途和作用。

1. __printf__函数:这是C语言中最常用的函数之一,用于向标准输出设备(通常是屏幕)打印输出信息。

它可以接受多个参数,包括格式控制字符串和要输出的数据,然后按照格式字符串的要求进行格式化输出。

例如,`printf("Hello, World!\n");`会在屏幕上输出"Hello, World!"。

2. __scanf__函数:与printf函数相反,scanf函数用于从标准输入设备(通常是键盘)读取输入数据。

它也可以接受多个参数,包括格式控制字符串和要读取的变量地址,然后根据格式字符串的要求从输入设备中读取数据并存储到相应的变量中。

例如,`scanf("%d", &num);`会从键盘上读取一个整数,并将其存储到变量num中。

3. __malloc__函数:在C语言中,动态内存分配是一种重要的技术,可以在程序运行时动态地分配和释放内存空间。

malloc函数就是用于分配内存空间的函数,它接受一个参数,即所需的内存空间大小(以字节为单位),然后返回一个指向分配的内存空间的指针。

例如,`int *ptr = (int *)malloc(sizeof(int));`会分配一个整数大小的内存空间,并将其地址存储到指针ptr中。

c语言中的几种输入方式

c语言中的几种输入方式

c语言中的几种输入方式
在C语言中,有几种常见的输入方式,包括:
1. scanf():这是最常用的输入函数,用于从标准输入(通常是键盘)读取
数据。

它的一般形式是 `scanf("%格式说明符", &变量)`。

例如,
`scanf("%d", &num)` 用于读取一个整数。

2. getchar():这个函数用于从标准输入读取一个字符。

3. gets():这个函数用于从标准输入读取一个字符串,直到遇到换行符或EOF。

但是,这个函数不检查输入长度,可能会导致缓冲区溢出,因此不推荐使用。

4. fgets():这个函数用于从指定的流中读取一行数据到缓冲区。

它的一般形式是 `fgets(buffer, size, stream)`,其中 `buffer` 是存储读取数据的缓冲区,`size` 是缓冲区的大小,`stream` 是要读取的流。

5. getline():这个函数用于从标准输入读取一行数据,包括换行符。

它的一般形式是 `getline(char lineptr, size_t n, FILE stream)`,其中 `lineptr` 是
一个指向字符指针的指针,用于存储读取的行的地址,`n` 是缓冲区的大小,`stream` 是要读取的流。

以上就是C语言中的几种常见输入方式。

C语言 中scanf详解

C语言 中scanf详解

C 中scanf ( ) 函数用法心得我觉得,在输入输出函数中,scanf()函数,应该是最麻烦的,有时它给我们的结果很可笑,但是一定是一原因的....首先声明一下,这篇日志不是介绍scanf()中各种格式符用法的文章(没有这个必要,但是大家一定要会用).我尝试了很多种输入,包括一些错误的练习,曾经对scanf()由迷茫转向清醒,又由清醒再次转向迷茫......不知道何时是个尽头,谁让C如此高深呢?在这里贴出来,也是想让自己时而不时能看到,也想知道自己的理解是否有错,错在哪里(所以我就厚着脸皮,放在上面了).注意, 键盘缓冲区与输入有着密切的关系,并且, 类型匹配对输入也极为重要!!下面进入主题:scanf对流的操作遵从类型匹配操作原则,如果类型不匹配,它将不读取输入流。

因此输入流将滞留,如果输入流不空,scanf不会等待用户输入,直接从缓冲区中输入. 但是,scanf() 怎样匹配? stdin又是什么?在网上搜到的关于匹配的非常少,有些细节原因还是找不到.所以,我自作主张的下了点结论:例: scanf("%d,%d",&i,&j); 输入:12 ,13回车但是,j!=13. //注意,12后有一个空格,why?原因:我解释为,在scanf()中,格式字符串中普通字符(不包括空白字符)实行的是严格匹配,因为格式串中%d后面是一个 ',' ,因此输入中数字12后必须为一个','.scanf("1123%s",&str); 输入:1123aaabb 时str为aaabb,但是,输入24aabbdd时, 会出错,因为1123必须进行严格匹配.另外: scanf("%d\n",&i); printf("i=%d",i); 要怎么输入才能输出:i=12 ? 它不是你想像中的那样,有机会尝试一下吧!一些样例:scanf()是一个有返回值的函数,它的返回值是什么?怎么样利用这个特性?scanf()中的匹配原则: 在本文第五点具体说明...scanf()中各种数据格式匹配的开始条件,结束条件 .如: %d ,\n等类型输入结束条件.scanf("%d\n",&i);printf("%d",i); 输入 12回车,并无输出,why? scanf()函数的结束条件: 当各个格式符匹配完毕,且最后有一个回车时,函数结束. scanf("%s",str)连续输入127个就不能继续输入了. //TC中,VC好像是4000多..//说明键盘缓冲区长度为一个字节吗?但是 stdin->bsize(缓冲区大小)事实上为512,这又是为什么?stdin缓冲区中的数据残留 : scanf("%3s",str); c= getchar(); 输入: aaabbccc回车, 此时str="aaa",c='b'; //缓冲区中数据残留!getch()不经过缓冲区,直接接收键盘上输入的字符.//在上例中,加上一个 ch=getch(); 但是getch()并不能读取bbccc中的任何一个,说明getch()与getchar()并不一样,并且它们对Enter读取的值也不同! 一个不常用的格式符: %[] ,如 scanf("%[a-z]",str);输入: abcdefdsaABCDEF 输出:str="abcdefdsa" ;怎么用scanf()来输入一个有空格的字符串?scanf()处理时,一个Enter送到缓冲区中有两个值 : 一个回车(10) ,一个换行(13). 可以用getchar()来接收(但是,在只能接收到\n,即13).在一个scanf()函数之后加个fflush(stdin)可以清除输入数据残留?scanf("%3s",str); fflush(stdin); c=getchar();直接输入aaabbbddd回车, c还能取得值吗?下面是详细解释:scanf()函数执行成功时的返回值是成功读取的变量数 , 也就是说,你这个scanf()函数有几个变量,如果scanf()函数全部正常读取,它就返回几。

c2-简单输入输出和数制

c2-简单输入输出和数制

一个完整的程序,常常要求具备输入输出功能。

C语言程序的输入输出功能是通过调用系统提供的标准函数(库函数)实现的。

格式化输入输出函数格式化输入输出函数按指定的格式完成输入输出过程。

1.输出函数printfprintf(格式控制串,输出表) 按照给定的输出格式、向标准输出设备输出信息输出项如printf("v=%f\n",a);其中"v=%f \n"是给定的格式控制串,而a是输出项,它们之间用逗号分隔。

格式控制串中用%打头后面跟一个字母的部分称为格式说明符,它规定了输出项的输出格式。

常用的格式说明符及其意义如下所示:%d 十进制整数(正数不输出符号)%f 浮点小数(实数)%x 十六进制整数%c 单一字符%s 字符串格式控制串:包含两种信息格式说明符:%格式字符,用于指定输出项输出格式,而是用一个具体的值代替输出普通字符或转义字符:原样输出上述的printf(),是把输出项a的值按%f规定的浮点小数形式显示出来,格式控制串中除转换说明符以外的其它字符都原封不动地输出到标准输出设备使用printf()函数可以有一个以上的输出项,这时格式控制串中的格式说明符与输出项的个数必须相同。

它们按各自的先后顺序一一对应例1 printf函数举例void main(){int a, b; //int是整型数据类型名定义2个整型变量a=10;b=25;printf("a=%d b=%d\n", a, b);printf("a+b=%d\n a-b=%d\n", a+b,a-b);}从上例中可以看出格式说明符不仅规定了输出格式,而且也决定了输出项的值在整个输出信息中的位置。

例如输出项a的输出位置就是格式控制串中与它对应的格式说明符的位置,即a=后面的%d的位置。

此外,从上面也可以看出,输出项可以是运算表达式,这时输出的是它的运算结果值2、输入函数scanfscanf(“格式控制串”,输入表) 从标准输入设备输入数据例如scanf("%d%d", &r,&h); //&取地址操作符格式控制串中一般只使用格式说明符,常用的格式说明符与前面printf函数中介绍的相同。

DSP 调用 C 输入输出函数 scanf() printf() gets() puts() zz

DSP 调用 C 输入输出函数 scanf() printf() gets() puts() zz

DSP 调用C 输入输出函数scanf() printf() gets() puts() zz1.1 标准输入输出函数1.1.1 格式化输入输出函数Turbo C2.0 标准库提供了两个控制台格式化输入、输出函数printf( ) 和scanf(), 这两个函数可以在标准输入输出设备上以各种不同的格式读写数据。

printf()函数用来向标准输出设备(屏幕)写数据; scanf() 函数用来从标准输入设备(键盘)上读数据。

下面详细介绍这两个函数的用法。

一、printf()函数printf()函数是格式化输出函数, 一般用于向标准输出设备按规定格式输出信息。

在编写程序时经常会用到此函数。

printf()函数的调用格式为:printf("&lt;格式化字符串&gt;", &lt;参量表&gt;);其中格式化字符串包括两部分内容: 一部分是正常字符, 这些字符将按原样输出; 另一部分是格式化规定字符, 以"%"开始, 后跟一个或几个规定字符,用来确定输出内容格式。

参量表是需要输出的一系列参数, 其个数必须与格式化字符串所说明的输出参数个数一样多, 各参数之间用","分开, 且顺序一一对应, 否则将会出现意想不到的错误。

1. 格式化规定符Turbo C2.0提供的格式化规定符如下:━━━━━━━━━━━━━━━━━━━━━━━━━━符号作用——————————————————————————%d 十进制有符号整数%u 十进制无符号整数%f 浮点数%s 字符串%c 单个字符%p 指针的值%e 指数形式的浮点数%x, %X 无符号以十六进制表示的整数%0 无符号以八进制表示的整数%g 自动选择合适的表示法━━━━━━━━━━━━━━━━━━━━━━━━━━说明:(1). 可以在"%"和字母之间插进数字表示最大场宽。

c语言程序设计教程第二版课后习题答案

c语言程序设计教程第二版课后习题答案

c语言程序设计教程第二版课后习题答案【篇一:c语言程序设计教程_李含光_郑关胜_清华大学出版社习题答案习题答案[完美打印版]】1.单项选择题(1)a (2)c(3)d (4)c (5)b 2.填空题(1)函数(2)主函数(main)(3)printf() , scanf()第2章习题参考答案1.单项选择题1-5 cbccc 6-10 cdcdc 11-13 dbb 2.填空题(1)1(2)26 (3)6 , 4 , 2 (4)10 , 6(5)3.000000 (6)双精度(double)(7)9 (8)字母,数字,下划线(9)13.700000 (10)11(11)((m/10)%10)*100+(m/100)*10+m%10(12)0 (13)10 ,9 ,11(15)(x0y0)||(x0z0)||(y0||z0)(16)double (17)x==0(18)sqrt(fabs(a-b))/(3*(a+b))(19)sqrt((x*x+y*y)/(a+b))第3章习题参考答案1.单项选择题1-5 cccdd 6-10 bcdbc11-15 bcbbb16 a 2.填空题(1)用;表示结束(2){ }(3)y=x0?1:x==0?0:-1(4)y%4==0y%100!=0||y%400==0(5)上面未配对(6)default 标号(7)while , do while , for(8)do while(9)本次(10)本层 3.阅读程序,指出结果(1)yes(2)*(3)abother(4)28 70(5)2,0(6)8(7)36 (8)1(9)3,1,-1,3,1,-1(10)a=12 ,y=12(11)i=6,k=4 (12)1,-2 4.程序填空(1)x:y , u:z(2)m=n , m!=0,m=m/10(3)teps , t*n/(2*n+1) , printf(“%lf\n”,2*s) (4)m%5==0 ,printf(“%d\n”,k) (5)cx=getchar() , cx!=front , cx(6)double s=0, 1.0/k , %lf (7)s=0 , sgmin, 5.编程题(1). #include stdio.h int main() {double x,y; scanf(%lf,x); if(x1) y=x;else if(x=1.0x10) y=2*x-11; elsey=3*x-11;printf(%lf\n,y); return 0; } (2).#include stdio.h int main() {double x,y,z,min;scanf(%lf%lf%lf,x,y,z); if(xy) min=y; else min=x; if(minz)min=z;printf(min=%lf\n,min); return 0; } (3).#include stdio.h int main() {int y,m,d,flag,s=0,w,i;scanf(%d%d%d,y,m,d);flag=(y%4==0y%100!=0||y%400==0);w=((y-1)*365+(y-1)/4-(y-1)/100+(y-1)/400)%7;for(i=1;i=m;i++) {switch(i) {case 1:s=d;break; case 2:s=31+d;break; case 3:s=59+d;break; case 4:s=90+d;break; case 5:s=120+d;break; case6:s=151+d;break; case 7:s=181+d;break; case8:s=212+d;break; case 9:s=243+d;break; case10:s=273+d;break; case 11:s=304+d;break; case12:s=334+d;break;} }s=(w+s)%7; if(s==0)printf(星期日\n); elseprintf(星期%d\n,s); return 0; }(4).#include stdio.h int main() {float p,r;scanf(%f,p); if(p=10) r=p*0.1;else if(p10p=20) r=10*0.1+(p-10)*0.075; else if(p20p=40)r=10*0.1+10*0.075+(p-20)*0.05; else if(p40p=60)r=10*0.1+10*0.075+20*0.05+(p-40)*0.03;else if(p60p=100)r=10*0.1+10*0.075+20*0.05+20*0.03+(p-60)*0.015; else if(p100)r=10*0.1+10*0.075+20*0.05+20*0.03+40*0.015+(p-100)*0.01; printf(%f\n,r); return 0; } (5).#include stdio.h int main() {char c;while((c=getchar())!=\n) {if(c=ac=z) c=c-32; putchar(c);}return 0; } (6).#includestdio.h int main() {int m,k=2;printf(输入一个正整数:\n); scanf(%d,m); while(km) if(m%k==0) {printf(%4d,k); m=m/k; } else k++;printf(%4d\n,m); return 0; } (7).#includestdio.h int main() {int a,n,s=0,p=0,i;scanf(%d %d,n,a); for(i=1;i=n;i++) {p=p*10+a; s=s+p; }printf(%d\n,s); return 0; } (8).#includestdio.h int main(){int i,j,k;for(i=1;i=9;i++) for(j=0;j=9;j++) for(k=0;k=9;k++)printf(%5d,100*i+10*j+k); return 0; }(9).#includestdio.h #includemath.h int main() {float a=-10,b=10,x,f1,f2,f; f1=(((2*a-4)*a+3)*a)-6; f2=(((2*b-4)*b+3)*b)-6; do {x=(a+b)/2;f=(((2*x-4)*x+3)*x)-6; if(f*f10) { b=x; f2=f; } else { a=x;f1=f; }}while(fabs(f)=1e-6); printf(%6.2f\n,x); return 0; }(10).#includestdio.h#includemath.h int main() {int n=2;double eps,t,s=0,x;scanf(%lf %lf,x,eps); t=x; s=t;while(fabs(t)=eps) {t=-t*(2*n-3)*x*x/(2*n-2); s=s+t/(2*n); n++; }printf(%d,%lf\n,n,s); return 0; }(11).#includestdio.h int main() {unsigned long s,t=0,p=1; scanf(%u,s); while(s!=0) {if((s%10)%2!=0) {t=t+(s%10)*p; p=p*10; }s=s/10; }printf(%u\n,t); return 0; }第4章习题参考答案1.单项选择题1-5 dddbd 6-10 badcd 11-14 bdab 2.填空题(1)2(2)嵌套,递归(3)全局变量,局部变量,静态变量,动态变量(4)auto , static , register , extern (5)外部变量(6)编译,运行 3.阅读程序,指出结果(1)15(2)5(3)5,4,3 (4)i=5 i=2 i=2 i=4 i=2(5)求水仙花数(6)-5*5*5(7)30 (8)0 10 1 11 2 124.程序填空(1)float fun(float , float) , x+y,x-y, z+y,z-y (2)x , x*x+1 (3)s=0 , a=a+b 5.编程题(1).while(s!=0) #includestdio.h { unsigned int fun(unsigned int);p=p+s%10; int main() s=s/10; { } unsigned int s; return p; scanf(%u,s); } printf(%u\n,fun(s)); (2). return 0;#includestdio.h } #includestdlib.h unsigned int fun(unsignedint s) #includemath.h { void f1(float,float,float,float); unsigned int p=0; void f2(float,float,float,float);【篇二:《c语言程序设计》课后习题答案(第四版)谭浩强】t>1.1什么是计算机程序11.2什么是计算机语言11.3c语言的发展及其特点31.4最简单的c语言程序51.4.1最简单的c语言程序举例61.4.2c语言程序的结构101.5运行c程序的步骤与方法121.6程序设计的任务141-5 #include stdio.hint main ( ){ printf (**************************\n\n);printf( very good!\n\n);printf (**************************\n);return 0;}1-6#include stdio.hint main(){int a,b,c,max;printf(please input a,b,c:\n);scanf(%d,%d,%d,a,b,c);max=a;if (maxb)max=b;if (maxc)max=c;printf(the largest number is %d\n,max);return 0;}第2章算法——程序的灵魂162.1什么是算法162.2简单的算法举例172.3算法的特性212.4怎样表示一个算法222.4.1用自然语言表示算法222.4.2用流程图表示算法222.4.3三种基本结构和改进的流程图262.4.4用n?s流程图表示算法282.4.5用伪代码表示算法312.4.6用计算机语言表示算法322.5结构化程序设计方法34习题36第章最简单的c程序设计——顺序程序设计37 3.1顺序程序设计举例373.2数据的表现形式及其运算393.2.1常量和变量393.2.2数据类型423.2.3整型数据443.2.4字符型数据473.2.5浮点型数据493.2.6怎样确定常量的类型513.2.7运算符和表达式523.3c语句573.3.1c语句的作用和分类573.3.2最基本的语句——赋值语句593.4数据的输入输出653.4.1输入输出举例653.4.2有关数据输入输出的概念673.4.3用printf函数输出数据683.4.4用scanf函数输入数据753.4.5字符数据的输入输出78习题823-1 #include stdio.h#include math.hint main(){float p,r,n;r=0.1;n=10;p=pow(1+r,n);printf(p=%f\n,p);return 0;}3-2-1#include stdio.h#include math.hint main(){float r5,r3,r2,r1,r0,p,p1,p2,p3,p4,p5;p=1000;r5=0.0585;r3=0.054;r2=0.0468;r1=0.0414;r0=0.0072;p1=p*((1+r5)*5);// 一次存5年期p2=p*(1+2*r2)*(1+3*r3); // 先存2年期,到期后将本息再存3年期 p3=p*(1+3*r3)*(1+2*r2); // 先存3年期,到期后将本息再存2年期p4=p*pow(1+r1,5); // 存1年期,到期后将本息存再存1年期,连续存5次 p5=p*pow(1+r0/4,4*5); // 存活期存款。

printf和scanf的用法(一)

printf和scanf的用法(一)

printf和scanf的用法(一)printf和scanf用法说明1. printf函数用法•printf函数是C语言中用于输出字符、字符串、数字等数据的函数。

•基本语法:printf("格式控制字符串", 参数列表);•格式控制字符串中可以包含普通的字符、转义字符和格式转换符。

普通字符输出•普通字符直接按照字符串的形式输出。

•示例:printf("Hello World!");转义字符输出•转义字符用于输出一些特殊字符,比如换行符、制表符等。

•示例:printf("Hello\nWorld!"); // 输出结果为两行printf("Hello\tWorld!"); // 输出结果中间有一个制表符格式转换符输出•格式转换符用于输出各种类型的数据,如整数、浮点数、字符、字符串等。

•常用格式转换符如下:–%d输出整数–%f输出浮点数–%c输出字符–%s输出字符串•示例:int num = 10;float pi = ;char ch = 'A';char str[] = "Hello World!";printf("num = %d\n", num); // 输出整数printf("pi = %.2f\n", pi); // 输出浮点数,保留两位小数printf("ch = %c\n", ch); // 输出字符printf("str = %s\n", str); // 输出字符串2. scanf函数用法•scanf函数是C语言中用于从标准输入读取字符、字符串、数字等数据的函数。

•基本语法:scanf("格式控制字符串", 参数列表);•格式控制字符串中可以包含普通的字符和格式转换符。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

自己来写printf()和scanf()函数一、预备知识——C语言中的可变参数函数1.先举一个例子:#define bufsize 80char buffer[bufsize];/**************************************************************** * 这个函数用来格式化带参数的字符串*****************************************************************/ int vspf(char *fmt, ...){va_list argptr; //声明一个转换参数的变量int cnt;va_start(argptr, fmt); //初始化变量//将带参数的字符串按照参数列表格式化到buffer中cnt = vsnprintf(buffer,bufsize ,fmt, argptr);va_end(argptr); //结束变量列表,和va_start成对使用return(cnt);}/**************************************************************** * 主函数*****************************************************************/ int main(int argc, char* argv[]){int inumber = 30;float fnumber = 90.0;char string[4] = "abc";vspf("%d %f %s", inumber, fnumber, string);printf("%s\n", buffer);return 0;}下面我们来探讨如何写一个简单的可变参数的C函数.2.写可变参数的C函数要在程序中用到以下这些宏: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在这里是variable-argument(可变参数)的意思。

这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件。

下面我们写一个简单的可变参数的函数,该函数至少有一个整数参数,第二个参数也是整数,是可选的。

函数的功能只是打印这两个参数的值。

void simple_va_fun(int i, ...){va_list arg_ptr;int j=0;va_start(arg_ptr, i);j=va_arg(arg_ptr, int);va_end(arg_ptr);printf("%d %d\n", i, j);return;}我们可以在我们的头文件中这样声明我们的函数:extern void simple_va_fun(int i, ...); 我们在程序中可以这样调用:simple_va_fun(100); simple_va_fun(100,200); 从这个函数的实现可以看到,我们使用可变参数应该有以下步骤:1)首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变量是指向参数的指针.2)然后用va_start宏初始化变量arg_ptr,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数,这里为参数i。

3)然后用va_arg返回可变的参数,并赋值给整数j 。

va_arg的第二个参数是你要返回的参数的类型,这里是int型。

4)最后用va_end宏结束可变参数的获取.然后你就可以在函数里使用第二个参数了。

如果函数有多个可变参数的,依次调用va_arg获取各个参数。

如果我们用下面三种方法调用的话,都是合法的,但结果却不一样:1)simple_va_fun(100); 结果是:100 -123456789(会变的值) 2)simple_va_fun(100,200); 结果是:100 200 3)simple_va_fun(100,200,300); 结果是:100 200 我们看到第一种调用有错误,第二种调用正确,第三种调用尽管结果正确,但和我们函数最初的设计有冲突.下面一节我们探讨出现这些结果的原因和可变参数在编译器中是如何处理的.3.可变参数在编译器中的处理我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的,由于1)硬件平台的不同 2)编译器的不同,所以定义的宏也有所不同,下面以VC++中stdarg.h里x86平台的宏定义摘录如下:typedef char * va_list;#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) #define va_end(ap) ( ap = (va_list)0 )定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.C语言的函数是从右向左压入堆栈的,图(1)是函数的参数在堆栈中的分布位置.我们看到va_list被定义成char*,有一些平台或操作系统定义为void*.再看va_start的定义,定义为&v+_INTSIZEOF(v),而&v是固定参数在堆栈的地址,所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在堆栈的地址,如图:图( 1 )然后,我们用va_arg()取得类型t的可变参数值,以上例为int型为例,我们看一下va_arg取int型的返回值:j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) ); 首先ap+=sizeof(int),已经指向下一个参数的地址了.然后返回ap-sizeof(int)的int*指针,这正是第一个可变参数在堆栈里的地址(图2).然后用*取得这个地址的内容(参数值)赋给j.图( 2 )最后要说的是va_end宏的意思,x86平台定义为ap=(char*)0;使ap不再指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的。

在这里大家要注意一个问题:由于参数的地址用于va_start宏,所以参数不能声明为寄存器变量或作为函数或数组类型。

关于va_start, va_arg, va_end的描述就是这些了,我们要注意的是不同的操作系统和硬件平台的定义有些不同,但原理却是相似的.4.可变参数在编程中要注意的问题因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能地识别不同参数的个数和类型.有人会问:那么printf中不是实现了智能识别参数吗?那是因为函数printf 是从固定参数format字符串来分析出参数的类型,再调用va_arg的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的.另外有一个问题,因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利.如果simple_va_fun()改为:void simple_va_fun(int i, ...){va_list arg_ptr;char *s=NULL;va_start(arg_ptr, i);s=va_arg(arg_ptr, char*);va_end(arg_ptr);printf("%d %s\n", i, s);return;}可变参数为char*型,当我们忘记用两个参数来调用该函数时,就会出现core dump(Unix) 或者页面非法的错误(window平台).但也有可能不出错,但错误却是难以发现,不利于我们写出高质量的程序。

以下提一下va系列宏的兼容性. System V Unix把va_start定义为只有一个参数的宏:va_start(va_list arg_ptr);而ANSI C则定义为:va_start(va_list arg_ptr, prev_param);如果我们要用system V的定义,应该用vararg.h头文件中所定义的宏,ANSI C的宏跟system V的宏是不兼容的,我们一般都用ANSI C,所以用ANSI C的定义就够了,也便于程序的移植。

二、自己写的STM32单片机printf()与scanf()函数include "stdarg.h"/************************************************************void MyPutChar(unsigned char ch)**通过串口输出一个字符**********************************************************/void MyPutChar(unsigned char ch){USART_SendData(USART1,ch);while(!(USART1->SR&USART_FLAG_TXE)); //等待发送完毕}/******************************************************************void my_printf(const char *fmt , ...)**格式化输出函数****************************************************************/void my_printf(const char *fmt /*format*/, ...){va_list arg_ptr; //声明一个转换参数的变量char arry[100]; //临时数组char *str; //临时数组指针va_start(arg_ptr, fmt); //初始化变量while((*fmt)!='\0'){if(*fmt == '\n' ){MyPutChar('\n');MyPutChar('\r');}else if(*fmt=='%'){fmt++;switch(*fmt){case 'd': //以十进制输出{dec2str(va_arg(arg_ptr, int),arry);str=arry;while(*str!='\0'){MyPutChar(*str); //发送一个字符}}break;case 's'://输出一个字符串{str=va_arg(arg_ptr, char*);while(*str!='\0'){MyPutChar(*str); //发送一个字符str++;}}break;default:break;}}else{MyPutChar(*fmt); //输出一个字符}fmt++;}va_end(arg_ptr); //结束变量列表,和va_start成对使用str=(char*)0;}/**************************************************************** **void my_scanf(const char *fmt , ...)**格式化输出函数****************************************************************/ void my_scanf(const char *fmt /*format*/, ...){va_list arg_ptr; //声明一个转换参数的变量int dec;int *d_ptr;char str[50];char *s_ptr;va_start(arg_ptr, fmt); //初始化变量while((*fmt)!='\0'){if(*fmt=='%'){fmt++;switch(*fmt){case 'd': //输入一个十进制数{MyGetStr(str);dec=str2int(str);d_ptr=va_arg(arg_ptr, int*);*d_ptr = dec;}break;case 's': //输入一个字符串{s_ptr=va_arg(arg_ptr, char*);MyGetStr(s_ptr);}break;default:break;}}else //什么也不做{}fmt++;}va_end(arg_ptr); //结束变量列表,和va_start成对使用s_ptr=(char*)0;d_ptr=(int*)0;}/**************************************************************** **int str2int(char *str)**将一个字符串装换为一个数值(十进制整数)**输入:str要转换的字符串,返回十进制数****************************************************************/ int str2int(char *str){int da = 0;if(*str == '-')//是负数{str++;while(*str != '\0'){if(*str>'0' && *str<'9'){da = da*10;da += *str-'0';}str++;}return (0-da);}else{while(*str != '\0'){if(*str>'0' && *str<'9'){da = da*10;da += *str-'0';}str++;}return (da);}}/**************************************************************** **void dec2str(int dec,char *str)**将一个十进制整数转换为一个字符串**dec:十进制数,str要转换的字符串****************************************************************/ void dec2str(int dec,char *str){char c;char *str1;if(dec<0){*(str++)='-';dec= - dec;//取正}str1=str;do{c = '0'+ dec %10;dec = dec/10;*(str++) = c;}while(dec!=0);*str='\0'; //字符串末尾加入‘\0'作为结束符str--;do{c=*str1;*(str1++)=*str;*(str--)=c;}while(str1<str);}/**************************************************************** **void MyGetStr(char *str)**从串口获得一个字符串****************************************************************/ void MyGetStr(char *str){while((*str=GetChar())!='\0'){str++;}}/**************************************************************** **char GetChar(void)**从串口获得一个字节,由my_scanf()函数调用****************************************************************/ char GetChar(void){while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==RESET);return ((char)(USART1->DR&0X1FF));}。

相关文档
最新文档