《第6讲_函数与编程(1).ppt》由会员分享,可在线阅读,更多相关《第6讲_函数与编程(1).ppt(29页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、C+程序设计数据类型与编程数据类型与编程(2)1 1 1 1 案例一案例一案例一案例一:课程类的设计课程类的设计课程类的设计课程类的设计2 2 2 2 案例二案例二案例二案例二:学生类的设计学生类的设计学生类的设计学生类的设计3 3 3 3 案例三案例三案例三案例三:教师类的设计教师类的设计教师类的设计教师类的设计4 4 4 4 深入学习一:关于深入学习一:关于深入学习一:关于深入学习一:关于main()main()main()main()函数函数函数函数5 5 5 5 深入学习二:函数参数的默认值深入学习二:函数参数的默认值深入学习二:函数参数的默认值深入学习二:函数参数的默认值6 6 6
2、6 深入学习三:可变参数函数深入学习三:可变参数函数深入学习三:可变参数函数深入学习三:可变参数函数1C+程序设计#include#include using namespace std;using namespace std;class Courseclass Course private:private:char cid8;char cid8;char cname20;char cname20;public:public:Course()cinCourse()cincidcidcnamecname;char*char*getcid()returngetcid()return cid;cid
3、;char*char*getcname()returngetcname()return cnamecname;void void setcid(charsetcid(char fc)strcpy(cid,fcfc)strcpy(cid,fc););void void setcname(charsetcname(char fcn)strcpy(cname,fcnfcn)strcpy(cname,fcn););void void display()coutdisplay()coutcid“”cid“”cnamecnameendlendl;1 案例一:课程类的设计案例一:课程类的设计2C+程序设计c
4、lass Student class Student char sid8;char sid8;char sname20;char sname20;intint sage;sage;boolbool ssexssex;public:public:Student();Student();getter;getter;setter;setter;void display();void display();2 案例二:学生类的设计案例二:学生类的设计3C+程序设计 class Teacher class Teacher char tid6;char tid6;char tname20;char tnam
5、e20;public:public:Teacher();Teacher();getter;getter;setter;setter;void display();void display();3 案例三:教师类的设计案例三:教师类的设计4C+程序设计p main()main()函数是函数是函数是函数是C+C+程序的入口函数程序的入口函数程序的入口函数程序的入口函数 p main()main()函数的返回值类型:函数的返回值类型:函数的返回值类型:函数的返回值类型:通常,通常,通常,通常,intint 或或或或 voidvoid,C+C+标准要求为标准要求为标准要求为标准要求为intint 当返
6、回值类型为当返回值类型为当返回值类型为当返回值类型为intint,而函数体内并没有出现,而函数体内并没有出现,而函数体内并没有出现,而函数体内并没有出现returnreturn语句语句语句语句时,同样可以通过编译并正常运行。时,同样可以通过编译并正常运行。时,同样可以通过编译并正常运行。时,同样可以通过编译并正常运行。原因:编译器在原因:编译器在原因:编译器在原因:编译器在main()main()函数的末尾自动添加了函数的末尾自动添加了函数的末尾自动添加了函数的末尾自动添加了return 0;return 0;的语句。的语句。的语句。的语句。其它的返回值类型不是其它的返回值类型不是其它的返回值
7、类型不是其它的返回值类型不是voidvoid的函数,如果没有使用的函数,如果没有使用的函数,如果没有使用的函数,如果没有使用returnreturn语句,编译器将报错。语句,编译器将报错。语句,编译器将报错。语句,编译器将报错。4 深入学习一:关于深入学习一:关于main()函数函数5C+程序设计p 其实,其实,其实,其实,main()main()函数的返回值类型可以是任意的类型。函数的返回值类型可以是任意的类型。函数的返回值类型可以是任意的类型。函数的返回值类型可以是任意的类型。例子:例子:例子:例子:float main()float main()coutcout“Hello!”“Hell
8、o!”endlendl;return 0.0;return 0.0;p 从程序员的角度:从程序员的角度:从程序员的角度:从程序员的角度:main()main()函数的主要作用是提供函数的主要作用是提供函数的主要作用是提供函数的主要作用是提供C+C+程序的入口,它的返回值程序的入口,它的返回值程序的入口,它的返回值程序的入口,它的返回值通常并不为程序员所关心。通常并不为程序员所关心。通常并不为程序员所关心。通常并不为程序员所关心。因此,某些编译器并没有强制性要求其返回值为因此,某些编译器并没有强制性要求其返回值为因此,某些编译器并没有强制性要求其返回值为因此,某些编译器并没有强制性要求其返回值为
9、intint,如,如,如,如VC+VC+,但有些则要求。,但有些则要求。,但有些则要求。,但有些则要求。然而,将其返回值类型设定为然而,将其返回值类型设定为然而,将其返回值类型设定为然而,将其返回值类型设定为intint,符合,符合,符合,符合C+C+标准,使程序具标准,使程序具标准,使程序具标准,使程序具有可移植性。有可移植性。有可移植性。有可移植性。4 深入学习一:关于深入学习一:关于main()函数函数6C+程序设计p 但是,但是,但是,但是,main()main()函数的返回值可以向操作系统提供程函数的返回值可以向操作系统提供程函数的返回值可以向操作系统提供程函数的返回值可以向操作系统
10、提供程序的运行状态信息。序的运行状态信息。序的运行状态信息。序的运行状态信息。在在在在WindowsWindows平台下,可通过环境变量平台下,可通过环境变量平台下,可通过环境变量平台下,可通过环境变量errorlevelerrorlevel获取用获取用获取用获取用C+C+开发的应用程序的开发的应用程序的开发的应用程序的开发的应用程序的main()main()函数的返回值,并依据此返回值做出函数的返回值,并依据此返回值做出函数的返回值,并依据此返回值做出函数的返回值,并依据此返回值做出不同的响应。不同的响应。不同的响应。不同的响应。例子:例子:例子:例子:#include#include in
11、tint main()main()intint i;i;std:coutstd:cout“please input a number:”;i;i;return i;return i;4 深入学习一:关于深入学习一:关于main()函数函数编译此程序生成编译此程序生成可执行文件可执行文件returnval.exereturnval.exe7C+程序设计编写一个批处理文件(如编写一个批处理文件(如编写一个批处理文件(如编写一个批处理文件(如test.battest.bat):):):):echo offecho offReturnvalReturnvalIf%If%errorlevelerrorl
12、evel%=3 echo third%=3 echo thirdIf%If%errorlevelerrorlevel%=2 echo second%=2 echo secondIf%If%errorlevelerrorlevel%=1 echo first%=1 echo first 当运行批处理文件,从控制台输入当运行批处理文件,从控制台输入当运行批处理文件,从控制台输入当运行批处理文件,从控制台输入1 1,得到输出,得到输出,得到输出,得到输出firstfirst,输入,输入,输入,输入2 2得到得到得到得到secondsecond。结论:结论:结论:结论:当程序当程序当程序当程序retu
13、rnvalreturnval运行时,运行时,运行时,运行时,main()main()函数的返回值存放在环境变函数的返回值存放在环境变函数的返回值存放在环境变函数的返回值存放在环境变量量量量errorlevelerrorlevel中,我们可以在批处理文件中利用这个返回值采中,我们可以在批处理文件中利用这个返回值采中,我们可以在批处理文件中利用这个返回值采中,我们可以在批处理文件中利用这个返回值采取不同的行动。取不同的行动。取不同的行动。取不同的行动。4 深入学习一:关于深入学习一:关于main()函数函数8C+程序设计p 关于关于关于关于exit(iexit(i)1 1)与)与)与)与retur
14、n i;return i;语句的执行结果相同。语句的执行结果相同。语句的执行结果相同。语句的执行结果相同。2 2)exit(iexit(i)的执行效果是返回操作系统,并将的执行效果是返回操作系统,并将的执行效果是返回操作系统,并将的执行效果是返回操作系统,并将i i作为程序的返作为程序的返作为程序的返作为程序的返回结果。回结果。回结果。回结果。3 3)当程序出现无法恢复的错误时,有可能使用)当程序出现无法恢复的错误时,有可能使用)当程序出现无法恢复的错误时,有可能使用)当程序出现无法恢复的错误时,有可能使用exit()exit()函数退函数退函数退函数退出程序。出程序。出程序。出程序。4 4)
15、在)在)在)在C+C+程序中,程序中,程序中,程序中,exit()exit()函数的使用会破坏程序对对象的析函数的使用会破坏程序对对象的析函数的使用会破坏程序对对象的析函数的使用会破坏程序对对象的析构函数的调用,控制得不好还会引发其他程序设计问题。构函数的调用,控制得不好还会引发其他程序设计问题。构函数的调用,控制得不好还会引发其他程序设计问题。构函数的调用,控制得不好还会引发其他程序设计问题。5 5)在)在)在)在C+C+程序设计中,应利用异常处理机制来取代对程序设计中,应利用异常处理机制来取代对程序设计中,应利用异常处理机制来取代对程序设计中,应利用异常处理机制来取代对exit()exit
16、()函数的使用。函数的使用。函数的使用。函数的使用。4 深入学习一:关于深入学习一:关于main()函数函数9C+程序设计p 关于命令行参数关于命令行参数关于命令行参数关于命令行参数 main()main()函数可以带参数,以处理由用户输入的命令行参数,函数可以带参数,以处理由用户输入的命令行参数,函数可以带参数,以处理由用户输入的命令行参数,函数可以带参数,以处理由用户输入的命令行参数,其格式固定为其格式固定为其格式固定为其格式固定为intint main(intmain(int argc,charargc,char*argvargv),其中,其中,其中,其中argcargc代表代表代表代表
17、参数的个数,参数的个数,参数的个数,参数的个数,argvargv数组中的每一个元素是一个保存命令行参数组中的每一个元素是一个保存命令行参数组中的每一个元素是一个保存命令行参数组中的每一个元素是一个保存命令行参数内容的字符串。数内容的字符串。数内容的字符串。数内容的字符串。#include#include using namespace std;using namespace std;intint main(intmain(int argc,charargc,char*argvargv)if(argcif(argc1)1)coutcout“Hello,”argv1“!”“Hello,”argv1
18、“!”endlendl;4 深入学习一:关于深入学习一:关于main()函数函数编译生成编译生成test.exe,运行输入,运行输入“test Jack”,输出:,输出:Hello,Jack!10C+程序设计p 关于命令行参数关于命令行参数关于命令行参数关于命令行参数 系统在提交命令行参数时,会自动在数组系统在提交命令行参数时,会自动在数组系统在提交命令行参数时,会自动在数组系统在提交命令行参数时,会自动在数组argvargv 的最后一的最后一的最后一的最后一个有效参数后面加一个空指针,这样,即使不知道个有效参数后面加一个空指针,这样,即使不知道个有效参数后面加一个空指针,这样,即使不知道个有
19、效参数后面加一个空指针,这样,即使不知道argcargc的值,的值,的值,的值,通过字符指针数组中空指针所处的位置,也能推断出命令行通过字符指针数组中空指针所处的位置,也能推断出命令行通过字符指针数组中空指针所处的位置,也能推断出命令行通过字符指针数组中空指针所处的位置,也能推断出命令行中有多少个参数。中有多少个参数。中有多少个参数。中有多少个参数。#include#include using namespace std;using namespace std;intint main(intmain(int argc,charargc,char*argvargv)intint i=0;i=0;
20、while(argviwhile(argvi)coutcoutargviargvi+“”;+“”;coutcoutendlendl;coutcout“argcargc=”=”argcargcendlendl;coutcout“i=”i“i=”iendlendl;4 深入学习一:关于深入学习一:关于main()函数函数11C+程序设计p 关于命令行参数关于命令行参数关于命令行参数关于命令行参数 编译生成执行文件编译生成执行文件编译生成执行文件编译生成执行文件nullp.exenullp.exe,然后执行,然后执行,然后执行,然后执行nullpnullp one one two threetwo
21、three,程序的输出是:,程序的输出是:,程序的输出是:,程序的输出是:nullpnullp one two three one two three argcargc=4=4 i=4 i=44 深入学习一:关于深入学习一:关于main()函数函数12C+程序设计p main()main()函数一定是程序中第一个被执行的函数吗?函数一定是程序中第一个被执行的函数吗?函数一定是程序中第一个被执行的函数吗?函数一定是程序中第一个被执行的函数吗?#include#include using namespace std;using namespace std;class A class A publi
22、c:public:A()A()coutcout“In constructor.”“In constructor.”endlendl;A a;A a;intint main()main()coutcout“In main()”“In main()”argcargcendlendl;4 深入学习一:关于深入学习一:关于main()函数函数输出结果:输出结果:In constructor.In main().13C+程序设计p main()main()函数一定是程序中第一个被执行的函数吗?函数一定是程序中第一个被执行的函数吗?函数一定是程序中第一个被执行的函数吗?函数一定是程序中第一个被执行的函数吗
23、?结论:结论:结论:结论:对象对象对象对象a a的构造函数先于的构造函数先于的构造函数先于的构造函数先于main()main()函数执行。函数执行。函数执行。函数执行。实际上:实际上:实际上:实际上:1 1)所有的外部对象的构造函数都是先于)所有的外部对象的构造函数都是先于)所有的外部对象的构造函数都是先于)所有的外部对象的构造函数都是先于main()main()函数执行函数执行函数执行函数执行的。的。的。的。2 2)如果要对类中的静态成员对象进行初始化,那么这些)如果要对类中的静态成员对象进行初始化,那么这些)如果要对类中的静态成员对象进行初始化,那么这些)如果要对类中的静态成员对象进行初始
24、化,那么这些对象的构造函数也是在对象的构造函数也是在对象的构造函数也是在对象的构造函数也是在main()main()函数之前执行的。函数之前执行的。函数之前执行的。函数之前执行的。3 3)如果在这些构造函数中还调用了其他函数的话,这些)如果在这些构造函数中还调用了其他函数的话,这些)如果在这些构造函数中还调用了其他函数的话,这些)如果在这些构造函数中还调用了其他函数的话,这些函数也先于函数也先于函数也先于函数也先于main()main()函数执行。函数执行。函数执行。函数执行。4 深入学习一:关于深入学习一:关于main()函数函数14C+程序设计p C+C+允许指定函数默认值允许指定函数默认
25、值允许指定函数默认值允许指定函数默认值 在一个程序中,如果需要多次用同样的参数值去调用函数,在一个程序中,如果需要多次用同样的参数值去调用函数,在一个程序中,如果需要多次用同样的参数值去调用函数,在一个程序中,如果需要多次用同样的参数值去调用函数,可将此参数值指定为函数参数默认值,从而不必每次调用时可将此参数值指定为函数参数默认值,从而不必每次调用时可将此参数值指定为函数参数默认值,从而不必每次调用时可将此参数值指定为函数参数默认值,从而不必每次调用时都给出这个参数值。都给出这个参数值。都给出这个参数值。都给出这个参数值。p使用函数默认值时要注意以下几点:使用函数默认值时要注意以下几点:使用函
26、数默认值时要注意以下几点:使用函数默认值时要注意以下几点:1 1)最好在函数的声明中指明参数的默认值,而在函数定义)最好在函数的声明中指明参数的默认值,而在函数定义)最好在函数的声明中指明参数的默认值,而在函数定义)最好在函数的声明中指明参数的默认值,而在函数定义时添加注释,这样会有较好的程序结构,否则,在分离编译时添加注释,这样会有较好的程序结构,否则,在分离编译时添加注释,这样会有较好的程序结构,否则,在分离编译时添加注释,这样会有较好的程序结构,否则,在分离编译模式下可能会出现问题。模式下可能会出现问题。模式下可能会出现问题。模式下可能会出现问题。例子:例子:例子:例子:5 深入学习二:
27、函数参数的默认值深入学习二:函数参数的默认值15C+程序设计pp例子:例子:例子:例子:/*/*defaultparm.cppdefaultparm.cpp*/*/#include#include#include”d.hinclude”d.h”using namespace std;using namespace std;void void f(intf(int x=10)x=10)coutcoutxxendlendl;intint main()main()f(29);f(29);f();f();UseDefaultUseDefault();();5 深入学习二:函数参数的默认值深入学习二:函
28、数参数的默认值/*/*d.hd.h*/*/void void f(intf(int x);x);void void UseDefaultUseDefault();();/*/*use.cppuse.cpp*/*/#include”#include”d.hd.h”void void UseDefaultUseDefault()()f();f();16C+程序设计p 使用函数默认值时要注意以下几点:使用函数默认值时要注意以下几点:使用函数默认值时要注意以下几点:使用函数默认值时要注意以下几点:该程序无法通过编译。该程序无法通过编译。该程序无法通过编译。该程序无法通过编译。因为:在源文件因为:在源文
29、件因为:在源文件因为:在源文件use.cppuse.cpp中,编译器只知道函数中,编译器只知道函数中,编译器只知道函数中,编译器只知道函数f()f()带有一个带有一个带有一个带有一个参数,它不知道函数参数,它不知道函数参数,它不知道函数参数,它不知道函数f()f()的参数有默认值。的参数有默认值。的参数有默认值。的参数有默认值。如果在头文件如果在头文件如果在头文件如果在头文件d.hd.h中,将函数中,将函数中,将函数中,将函数f()f()的声明改为的声明改为的声明改为的声明改为void void f(intf(int x=10)x=10),则仍然会有编译错误。原因是不能够同时在函数声明和函,则
30、仍然会有编译错误。原因是不能够同时在函数声明和函,则仍然会有编译错误。原因是不能够同时在函数声明和函,则仍然会有编译错误。原因是不能够同时在函数声明和函数定义中都指定参数的默认值。数定义中都指定参数的默认值。数定义中都指定参数的默认值。数定义中都指定参数的默认值。结论:结论:结论:结论:在函数声明中指定函数参数的默认值,而在函数定义时不在函数声明中指定函数参数的默认值,而在函数定义时不在函数声明中指定函数参数的默认值,而在函数定义时不在函数声明中指定函数参数的默认值,而在函数定义时不再指定,用注释描述一下就可以了。再指定,用注释描述一下就可以了。再指定,用注释描述一下就可以了。再指定,用注释描
31、述一下就可以了。5 深入学习二:函数参数的默认值深入学习二:函数参数的默认值17C+程序设计p使用函数默认值时要注意以下几点:使用函数默认值时要注意以下几点:使用函数默认值时要注意以下几点:使用函数默认值时要注意以下几点:2 2)如果一个函数有多个参数有默认值,则任何一个不带默)如果一个函数有多个参数有默认值,则任何一个不带默)如果一个函数有多个参数有默认值,则任何一个不带默)如果一个函数有多个参数有默认值,则任何一个不带默认值的参数都要出现在所有带默认值的参数的左边,只有这认值的参数都要出现在所有带默认值的参数的左边,只有这认值的参数都要出现在所有带默认值的参数的左边,只有这认值的参数都要出
32、现在所有带默认值的参数的左边,只有这样才能保证在函数调用时不发生参数匹配时的二义性。样才能保证在函数调用时不发生参数匹配时的二义性。样才能保证在函数调用时不发生参数匹配时的二义性。样才能保证在函数调用时不发生参数匹配时的二义性。例子:例子:例子:例子:void void func(intfunc(int x=10,int x=10,int y,inty,int z=11)z=11)调用时调用时调用时调用时 func(5,6)func(5,6)3 3)函数参数的默认值虽然给程序员带来了一定程度的便利,)函数参数的默认值虽然给程序员带来了一定程度的便利,)函数参数的默认值虽然给程序员带来了一定程度
33、的便利,)函数参数的默认值虽然给程序员带来了一定程度的便利,也可能造成函数重载时的二义性。也可能造成函数重载时的二义性。也可能造成函数重载时的二义性。也可能造成函数重载时的二义性。例子:例子:例子:例子:5 深入学习二:函数参数的默认值深入学习二:函数参数的默认值18C+程序设计pp例子:例子:例子:例子:#include#include using namespace std;using namespace std;void void show(intshow(int x=10,double y=9.8)x=10,double y=9.8)coutcoutx“”yx“”yendlendl;v
34、oid void show(intshow(int x)x)coutcoutxxendlendl;intint main()main()show();show();show(3,4,5);show(3,4,5);show(6);show(6);5 深入学习二:函数参数的默认值深入学习二:函数参数的默认值19C+程序设计 该程序无法通过编译。该程序无法通过编译。该程序无法通过编译。该程序无法通过编译。原因:函数调用原因:函数调用原因:函数调用原因:函数调用show(6)show(6)有二义性。有二义性。有二义性。有二义性。不知是调用不知是调用不知是调用不知是调用void void show(in
35、tshow(int)还是调用还是调用还是调用还是调用void void show(int,doubleshow(int,double)5 深入学习二:函数参数的默认值深入学习二:函数参数的默认值20C+程序设计p一般的一般的一般的一般的C/C+C/C+函数,其参数个数在函数定义的时函数,其参数个数在函数定义的时函数,其参数个数在函数定义的时函数,其参数个数在函数定义的时候就确定了。候就确定了。候就确定了。候就确定了。p但是,在但是,在但是,在但是,在C C语言中就允许定义参数个数可变的函数,语言中就允许定义参数个数可变的函数,语言中就允许定义参数个数可变的函数,语言中就允许定义参数个数可变的函
36、数,如如如如printfprintf()()。p可变参数函数可以简称为可变参数函数可以简称为可变参数函数可以简称为可变参数函数可以简称为vava函数(函数(函数(函数(variable variable argument functionargument function)p在在在在C+C+中,也可以定义和使用可变参数函数。中,也可以定义和使用可变参数函数。中,也可以定义和使用可变参数函数。中,也可以定义和使用可变参数函数。p例子:例子:例子:例子:6 深入学习三:可变参数函数深入学习三:可变参数函数21C+程序设计#include#include using namespace std;us
37、ing namespace std;void void PrintAll(intPrintAll(int n1,)n1,)intint*ptrptr;ptrptr=&n1;=&n1;while(*while(*ptrptr)coutcout*ptrptrendlendl;ptrptr+;+;intint main()main()PrintAll(3,4,5,0);PrintAll(3,4,5,0);6 深入学习三:可变参数函数深入学习三:可变参数函数输出结果:输出结果:34522C+程序设计p要定义和使用可变参数的函数,要解决如下问题:要定义和使用可变参数的函数,要解决如下问题:要定义和使用可
38、变参数的函数,要解决如下问题:要定义和使用可变参数的函数,要解决如下问题:1 1)可变参数函数的函数头的书写形式)可变参数函数的函数头的书写形式)可变参数函数的函数头的书写形式)可变参数函数的函数头的书写形式 2 2)如何依次取出各个参数)如何依次取出各个参数)如何依次取出各个参数)如何依次取出各个参数 3 3)如何判断实际上有多少参数传递给了该函数)如何判断实际上有多少参数传递给了该函数)如何判断实际上有多少参数传递给了该函数)如何判断实际上有多少参数传递给了该函数p带有一个固定参数的可变参数头的形式:带有一个固定参数的可变参数头的形式:带有一个固定参数的可变参数头的形式:带有一个固定参数的
39、可变参数头的形式:return_typereturn_type func_name(para_typefunc_name(para_type para1,)para1,)因为,在函数体内部,必须先获取某个参数的地址,然后因为,在函数体内部,必须先获取某个参数的地址,然后因为,在函数体内部,必须先获取某个参数的地址,然后因为,在函数体内部,必须先获取某个参数的地址,然后根据其他参数与该参数的相对位置来获取其他参数。根据其他参数与该参数的相对位置来获取其他参数。根据其他参数与该参数的相对位置来获取其他参数。根据其他参数与该参数的相对位置来获取其他参数。6 深入学习三:可变参数函数深入学习三:可变参
40、数函数23C+程序设计p确定函数的可变参数的实际个数和各个参数的实际确定函数的可变参数的实际个数和各个参数的实际确定函数的可变参数的实际个数和各个参数的实际确定函数的可变参数的实际个数和各个参数的实际类型,是一件困难的事情。类型,是一件困难的事情。类型,是一件困难的事情。类型,是一件困难的事情。因为,在定义函数的时候,无法预料实际调用时的情形。因为,在定义函数的时候,无法预料实际调用时的情形。因为,在定义函数的时候,无法预料实际调用时的情形。因为,在定义函数的时候,无法预料实际调用时的情形。因此,只能从逻辑上做某种约定,然后根据约定进行判断。因此,只能从逻辑上做某种约定,然后根据约定进行判断。
41、因此,只能从逻辑上做某种约定,然后根据约定进行判断。因此,只能从逻辑上做某种约定,然后根据约定进行判断。pC C编译器为了统一在不同的硬件平台上的实现,增编译器为了统一在不同的硬件平台上的实现,增编译器为了统一在不同的硬件平台上的实现,增编译器为了统一在不同的硬件平台上的实现,增加代码的可移植性,提供了一系列宏来屏蔽硬件环境加代码的可移植性,提供了一系列宏来屏蔽硬件环境加代码的可移植性,提供了一系列宏来屏蔽硬件环境加代码的可移植性,提供了一系列宏来屏蔽硬件环境不同带来的差异。不同带来的差异。不同带来的差异。不同带来的差异。ANSI CANSI C标准下,标准下,标准下,标准下,vava的宏定义
42、在的宏定义在的宏定义在的宏定义在stdarg.hstdarg.h中。它们有:中。它们有:中。它们有:中。它们有:va_listva_list va_startva_start()()va_argva_arg()()va_endva_end()()6 深入学习三:可变参数函数深入学习三:可变参数函数24C+程序设计#include#include using namespace std;using namespace std;#include#include intint SqSum1(int n1,)SqSum1(int n1,)va_listva_list arg_ptrarg_ptr;in
43、tint nSqSumnSqSum=0,n=n1;=0,n=n1;va_start(arg_ptr,n1);va_start(arg_ptr,n1);while(nwhile(n0)0)nSqSumnSqSum+=(n*n);+=(n*n);n=n=va_arg(arg_ptr,intva_arg(arg_ptr,int););va_end(arg_ptrva_end(arg_ptr););return return nSqSumnSqSum;6 深入学习三:可变参数函数深入学习三:可变参数函数intint SqSum2(int n1,)SqSum2(int n1,)va_listva_lis
44、t arg_ptrarg_ptr;intint nSqSumnSqSum=0,n;=0,n;va_start(arg_ptr,n1);va_start(arg_ptr,n1);while(n10)while(n10)n=n=va_arg(arg_ptr,intva_arg(arg_ptr,int););nSqSumnSqSum+=(n*n);+=(n*n);n1-;n1-;va_end(arg_ptrva_end(arg_ptr););return return nSqSumnSqSum;25C+程序设计 intint main()main()intint nSqSumnSqSum=SqSum
45、1(1,1,1,1,0);=SqSum1(1,1,1,1,0);coutcoutnSqSumnSqSumendlendl;nSqSumnSqSum=SqSum2(4,1,1,1,1);=SqSum2(4,1,1,1,1);coutcoutnSqSumnSqSumendlendl;6 深入学习三:可变参数函数深入学习三:可变参数函数 输出结果:输出结果:输出结果:输出结果:4 44 426C+程序设计pVaVa相关的宏定义,不只有一组,但在各组定义前都会相关的宏定义,不只有一组,但在各组定义前都会相关的宏定义,不只有一组,但在各组定义前都会相关的宏定义,不只有一组,但在各组定义前都会有条件编译指
46、令。条件编译指示的是不同硬件平台和编有条件编译指令。条件编译指示的是不同硬件平台和编有条件编译指令。条件编译指示的是不同硬件平台和编有条件编译指令。条件编译指示的是不同硬件平台和编译器下用怎样的译器下用怎样的译器下用怎样的译器下用怎样的vava宏定义。宏定义。宏定义。宏定义。在在在在X86X86平台上的相关宏定义如下:平台上的相关宏定义如下:平台上的相关宏定义如下:平台上的相关宏定义如下:#define _#define _INFSIZEOF(nINFSIZEOF(n)(sizeof(n)+sizeof(int)-1)&)(sizeof(n)+sizeof(int)-1)&(sizeof(in
47、t)-1)(sizeof(int)-1)#define#define va_start(ap,vva_start(ap,v)()(apap=(=(va_list)&v+_INTSIZEOF(vva_list)&v+_INTSIZEOF(v)#define#define va_arg(ap,tva_arg(ap,t)(*(t*)()(*(t*)(apap+=_+=_INTSIZEOF(t)-INTSIZEOF(t)-_INTSIZEOF(t_INTSIZEOF(t)#define#define va_end(apva_end(ap)()(apap=(va_list)0)=(va_list)0)6
48、 深入学习三:可变参数函数深入学习三:可变参数函数27C+程序设计含义如下:含义如下:含义如下:含义如下:(1)(1)把把把把va_listva_list定义成定义成定义成定义成char*char*,是因为在,是因为在,是因为在,是因为在X86X86平台上,字符指针类平台上,字符指针类平台上,字符指针类平台上,字符指针类型可以用来指向任何一个内存单元的地址(因为型可以用来指向任何一个内存单元的地址(因为型可以用来指向任何一个内存单元的地址(因为型可以用来指向任何一个内存单元的地址(因为sizeof(charsizeof(char)=1)=1),在有些机器上),在有些机器上),在有些机器上),在
49、有些机器上va_listva_list是被定义成是被定义成是被定义成是被定义成void*void*的。的。的。的。(2)(2)定义定义定义定义_ _INTSIZEOF(nINTSIZEOF(n)主要是为了在某些需要内存对齐的系统主要是为了在某些需要内存对齐的系统主要是为了在某些需要内存对齐的系统主要是为了在某些需要内存对齐的系统中,获取某个参数的实际内存大小。从宏的名字可以看出是同整中,获取某个参数的实际内存大小。从宏的名字可以看出是同整中,获取某个参数的实际内存大小。从宏的名字可以看出是同整中,获取某个参数的实际内存大小。从宏的名字可以看出是同整型量对齐。一般型量对齐。一般型量对齐。一般型量
50、对齐。一般sizeof(intsizeof(int)=4)=4,则参数在内存中的地址都为,则参数在内存中的地址都为,则参数在内存中的地址都为,则参数在内存中的地址都为4 4的的的的倍数。通过计算可知,如果倍数。通过计算可知,如果倍数。通过计算可知,如果倍数。通过计算可知,如果sizeof(nsizeof(n)在在在在1-41-4之间,那么之间,那么之间,那么之间,那么_ _INTSIZEOF(nINTSIZEOF(n)=4)=4;如果;如果;如果;如果sizeof(nsizeof(n)在在在在5-85-8之间,那么之间,那么之间,那么之间,那么_ _INTSIZEOF(nINTSIZEOF(n