04_Function

主要内容:函数的定义、声明与调用 函数间的参数传递 函数嵌套与内联函数 形参带缺省值的函数与函数重载 数据的作用域 预编译处理与多文件结构 C++系统函数
展开查看详情

1.第四讲 函 数

2.2 主要内容 函数的 定义、 声明 与 调用 函数间的参数传递 函数嵌套与内联函数 形参 带缺省值的函数与函数重载 数据的作用域 预编译处理与多文件结构 C++ 系统函数

3.3 函数 的定义 函数是程序设计中,对功能的 抽象,是 C++ 的基本模块 C++ 程序是由函数构成的(一个或多个函数) C++ 程序必须有且只能有一个 main 函数 函数头 函数体 函数的定义 类型标识符 函数名( 形式参数表 ) { 语句 } 类型标识符 指明了本函数的类型,即函数返回值的类型 若没有返回值,则可以使用 void

4.4 形式参数表 形式参数表 ( 形参 ) 要指定数据类型 有多个形参时,用逗号隔开,每个形参需单独指定数据类型 如果函数 不带参数 ,则 形式参数 可以省略,但括号不能省 形参 只在 函数内部 有效 / 可见 类型标识符 变量 , 类型标识符 变量 , . . . . . . 函数返回值 int my_max(int x, int y) // OK int my_max(int x, y) // ERROR 通过 return 语句给出,如 return x 若没有返回值,可以不写,也可以写不带表达式的 return 形式参数列表

5.5 函数定义举例 int my_max(int x, int y) { if (x>y) return x; else return y; } 例: ex04_fun_01.cpp int my_sum(int x, int y) { return x+y; }

6.6 函数的调用 函数调用前须先声明 类型标识符 函数 名 ( 形式参数表 ) ; 可以在调用函数中,或程序文件中所有函数之外声明 主调函数与被调函数 被调函数在主调函数后定义,须在调用前声明 被调函数在主调函数前定义,则主调函数中可以直接调用 函数的调用方式 函数名( 实参数表 ) 被调函数可以出现在表达式中,此时必须要有返回值 ex04_fun_02.cpp

7.7 函数的调用过程 int main() { ... fun1() ; ... fun2() ; ... } int fun1() { ... fun3() ; ... } int fun2() { ... } int fun3() { ... }

8.8 函数的调用举例 例: 输入一个二进制数,输出相应的十进制数 ex04_fun_03.cpp 思考:如何计算 1111 1111 1111 1111 对应的十进制数 ? ( 提示:字符数组 ) 例: 利用 sin 函数的 Taylor 展开计算 sin(pi/2) 的值 (直到级数某项的绝对值小于 10 -15 为止) ex04_mysin.cpp 思考:利用上面的公式计算 sin(41*pi/2) ,结果如何?

9.9 函数的调用举例 例: 找出 11~999 之间的数 m ,满足 m 、 m 2 和 m 3 均为回文数 ex04_fun_huiwen.cpp 分析: 利用除以 10 取余的方法,从最低位开始,依次取出该数的各位数字。按反序重新构成新的数,比较与原数是否相等,若相等,则原数为回文数 回文数:各位数字左右对称的整数,如 11 , 121 , 1331

10.10 举例:随机数 例: 随机数的生成 seed=11; srand (seed); % 设置种子 x= rand() ; % 返回一个随机整数 rand() : 返回 一个 0 ~ RAND_MAX 之间的伪随机整数 srand ( seed ) : 设置种子 。如不设定,默认种子为 1 相同的种子对应相同的伪随机整数 每次执行 rand() 后,种子会自动改变 , 但变化规律是固定的 思考:如何生成 [a, b] 之间的随机整数 思考:如何生成 [0, 1] 之间的随机小数 ex04_rand_02.cpp ex04_rand_03.cpp 需包含头文件 cstdlib ex04_rand_01.cpp

11.11 举例:计时函数 #include < ctime > . . . . . . clock_t t0, t 1 ; double totaltime ; . . . . . . t0 = clock() ; . . . . . . t 1 = clock() ; totaltime =(double)(t 1 - t0) / CLOCKS_PER_SEC ; clock() : 返回 进程启动后所使用的 cpu 总毫秒数 ex04_time_01.cpp 需包含头文件 ctime 例: 计时函数: clock

12.12 举例:计时函数 #include <ctime> . . . . . . time _t t0, t 1 ; t0 = time(NULL) ; . . . . . . t 1 = time(NULL) ; t = t1 - t0; time(NULL) :返回从 1970 年 1 月 1 日 0 时至今的秒数 ex04_time_02.cpp clock 以毫秒为单位, time 以秒为单位 例: 计时函数: time 需包含头文件 ctime

13.13 猜数游戏 由计算机随机产生 [1,100] 之间的一个整数,然后由用户猜测这个数。要求根据用户的猜测情况给出不同的提示:如果猜测的数大于产生的数,则显示 Larger ;小于则显示 Smaller ;等于则显示 You won! 同时退出游戏。用户最多有 7 次机会。 ex04_rand_04.cpp Tips :如何生成每次都不同的随机整数? srand (time(NULL)); // srand (time(0 )) x = rand(); 例: 猜数游戏

14.14 函数的参数传递机制 形参 只在函数被调用时才分配存储单元,调用结束即被释放 实参可以是常量、变量、表达式、函数 ( 名 ) 等,但它们必须要有确定的值,以便把这些值传送给形参 实参和形参在 数量 、 类型 、 顺序 上应严格一致 传递时是将实参的值传递给对应的形参,即 单向传递 形参获得实参传递过来的值后,便与实参脱离关系, 即此后 形参的值的改变不会影响实参的值 传递方式一: 值 传递

15.15 举例 int main() { ... ... x = 3.0; n = 2; y = my_power (x, n); ... ... } ex04_fun_11.cpp double my_power (double x, int k) { ... ... } 思考:编写一个函数,交换两个整数的值 ex04_fun_12.cpp

16.16 引用 引用 是一种特殊类型的变量,可看作是变量的 别名 如何在改变形参的值的同时改变实参的值? 引用传递 例: 引用的声明与使用: & int a; int &ra = a; // 声明一个指向 a 的引用 a = 3; cout << "a=" << a << endl; ra = 5; // ra 和 a 共享同一个存储空间 cout << "a=" << a << endl; ex04_fun_13.cpp

17.17 引用 声明一个 引用 时 必须初始化 ,指向一个存在的对象 引用一旦初始化就不能改变,即不能再指向其它对象 若 引用作为形参 ,则调用函数时 才会被 初始化,此时形参是实参的一个别名,对形参的任何操作也会直接作用于实参 void swap_new(int &a, int &b) { int t = a; a = b; b = t; } int main() { int x=5, y=8; cout << "x=" << x << ", y=" << y << endl; swap_new(x, y); cout << "x=" << x << ", y=" << y << endl; } ex04_fun_14.cpp

18.18 函数的嵌套 函数可以嵌套调用,但不能嵌套定义 函数也可以递归调用(函数可以直接或间接调用自己) ex04_factorial.cpp 例: 利用右边的公式计算阶乘: 注意: 对同一个函数的多次不同调用中,编译器会给函数的形参和局部变量分配不同的空间,它们互不影响

19.19 递归

20.20 举例:汉诺塔 例: 汉诺塔问题。 有 三根针 A 、 B 、 C 。 A 针上有 N 个盘子,大的在下,小的在上,要求把这 N 个盘子从 A 针移到 C 针,在移动过程中可以借助 B 针,每次只允许移动一个盘,且在移动过程中在三根针上都保持 大盘在下,小盘在上 。 A B C

21.21 举例:汉诺塔 A B C A B C A B C A B C A B C

22.22 举例:汉诺塔 分析: 该问题可分解为下面三个步骤: (1) 将 A 上 n - 1 个盘子移到 B 针上(借助 C 针) ; (2) 把 A 针上剩下的一个盘子移到 C 针上 ; (3) 将 n - 1 个盘子从 B 针移到 C 针上(借助 A 针) ; 上面三个步骤包含两种操作: ① 将多个盘子从一个针移到另一个针上, 这是一个递归的过程,我们用 hanoi 函数实现。 ② 将 1 个盘子从一个针上移到另一针上, 该过程用 move 函数实现。 ex04_hanoi.cpp

23.23 内联函数 定义与普通函数一样,只需加关键字 inline 编译时在调用处用函数体进行 替换 使用内联函数能节省参数传递、控制转移等开销,提高代码的执行效率 内联函数声明与使用 内联函数通过应该 功能简单、规模小、使用频繁 内联函数体内 不建议使用循环语句和 switch 语句 有些函数无法定义成内联函数,如递归调用函数等 ex04_inline.cpp

24.24 形参带缺省值的函数 函数在定义时可以预先给出缺省的形参值,调用时如给出实参,则采用实参值,否则采用预先给定的缺省形参值 特点:调用时可以 不提供 或 提供部分实参 形参 缺省值 int add(int x=5, int y=6) { return x+y; } int main() { add(10,20); //10+20 add(); //5+6 add(10); //10+6 } 例:

25.25 缺省形参 值的声明 缺省形参值必须 从右向左 顺序声明 在缺省形参值的右面不能有非缺省形参值的参数,因为调用时实参取代形参是从左向右的顺序 缺省形参值的声明次序 int add(int x, int y=5, int z=6); // 正确 int add(int x=1, int y=5, int z); // 错误 int add(int x=1, int y, int z=6); // 错误 例:

26.26 缺省形参值的位置 当被调函数出现在主调函数之前时,缺省形参值必须在函数定义时给出 当被调函数出现在主调函数之后时,缺省形参值需在函数声明时给出 例: int add(int x=5, int y=6) ; int main() { add(); // 调用在实现前 } int add(int x, int y) { return x+y; } int add(int x=5,int y=6) { return x+y; } int main() { add(); // 调用在实现后 } ex04_fun_16.cpp 注意: 这里不能再出现缺省形参!

27.27 缺省形参值的作用域 在相同的作用域内(同一对大括号所界定的范围),缺省形参值的说明应保持惟一,但如果在不同的作用域内,允许说明不同的缺省形参 例: int add(int x=1, int y=2); int main() { int add(int x=3,int y=4); add(); // 使用局部缺省形参值(实现 3+4 ) } void fun() { ... add(); // 使用全局缺省形参值(实现 1+2 ) } ex04_fun_17.cpp

28.28 函数重载 C++ 允许 功能相近 的函数在相同的作用域内以 相同函数名 声明,从而形成重载。方便使用,便于记忆。 两个以上的函数,具有相同的函数名,但形参的个数或类型不同,编译器会根据实参和形参的个数和类型的最佳匹配,自动确定调用哪一个函数,这就是 函数重载 例: 整数的加法与实数的加法。 ex04_overload.cpp

29.29 函数重载 重载函数的形参必须不同: 个数不同 或 类型不同 形参类型不同 int add(int x, int y); double add(double x, double y); 形参个数不同 int add( int x, int y); int add( int x, int y, int z); 如果函数名相同,形参个数与类型也相同,则无论函数返回值的类型是否相同,编译时会认为语法错误! 不要将功能不同的函数定义为重载函数! 在使用带有默认形参的重载函数时,要注意防止二义性! int add(int x, int y=1); int add(int x); add(10); ?