1.8 参数和函数
参数也叫参变量,它就是一个变量,传递参数就是传递变量。关于参数,可以这样去理解:魔法师可以根据观众的要求变出鸡、鸭、猫等动物,当观众要求魔法师变出鸡时,观众说出的“鸡”对魔法师而言就是参数。
函数是一组一起执行一个任务的语句。每个C++程序都至少有一个函数,即主函数 main(),所有程序都可以定义其他额外的函数。可以把代码划分到不同的函数中,如何划分代码到不同的函数中可自行决定,但在逻辑上,划分通常是根据每个函数执行一个特定的任务进行的。函数的声明告诉编译器函数的名称、返回类型和参数。函数的定义提供了函数的实际主体。
· 1.8.1 函数的定义
函数的定义一般主要由5个部分组成:返回值类型、函数名、参数列表、函数体语句、return 表达式。
具体语法如下。
返回值类型 函数名 (参数列表)
{
函数体语句
return表达式
}
● 返回值类型 :一个函数可以返回一个值,有些函数只是执行所需的操作而不返回值,在这种情况下,返回值类型是void。
● 函数名:这是函数的实际名称,自定义函数名须满足变量名命名规范。
● 参数列表:使用该函数时传入的数据。
● 函数体语句:“{}”内的代码,函数内需要执行的语句。
● return表达式:和返回值类型有关,函数执行完后,返回相应的数据。
具体定义方法见例1-29。
例1-29:定义一个加法函数,实现两个数相加。
//函数定义
#include <iostream>
using namespace std;
int add(int num1, int num2)//包括了2个形式参数(简称形参)
{
int sum = num1 + num2;
return sum;//函数返回值
}
· 1.8.2 函数调用
功能:创建C++函数时,会定义函数的任务,然后通过调用函数来完成已定义的任务。当程序调用函数时,程序控制权会被转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束标记花括号位置时,会把程序控制权交还给主程序。调用函数时,传递所需参数,如果函数返回一个值,则可以存储该返回值。
语法:函数名(参数)。
具体调用方法见例1-30。
例1-30:函数调用。
#include<iostream>
using namespace std;
//函数定义
int add(int num1, int num2) //定义中的num1、num2称为形式参数,简称形参
{
int sum = num1 + num2;
return sum;
}
int main() {
int a = 10;
int b = 10;
//调用add()函数
int sum = add(a, b);//调用时的a、b称为实际参数,简称实参
cout << "sum = " << sum << endl;
a = 100;
b = 100;
sum = add(a, b);
cout << "sum = " << sum << endl;
return 0;
}
例1-30运行结果如图1-49所示。
图1-49 例1-30运行结果
总结:函数定义里圆括号内的参数称为形式参数(简称形参),函数调用时传入的参数称为实际参数(简称实参)。
· 1.8.3 值传递
C语言的参数传递方法有3种:值传递、地址传递、引用传递,具体如下。
值传递:在函数调用时,实参以数值形式传给形参。在进行值传递时,如果形参发生改变,并不会影响实参。
地址传递:在调用函数的时候,将参数的值所在的地址复制一份过去。因此,被调用函数对参数地址的值进行修改会影响原来的值。
地址传递是比较难理解的概念,对此进行对比说明:关于值传递,可这样去理解,子函数以形参形式从主函数中传入数据,但这个数据只和形参发生传值后就立刻返回,而后子函数发生的数据变化不会影响主函数的数据;关于地址传递,子函数以形参形式从主函数中传入的是存放数据的地址,因此子函数的数据变化就是主函数的数据变化。
进一步通过“孙悟空”和“杨戬”来举例说明。定义孙悟空为一个变量,杨戬为一个函数,变量孙悟空可利用分身术产生一个形参传递给函数杨戬,这时候为值传递。函数杨戬怎样收拾变量孙悟空的分身(形参,只传递值的副本)都不会对变量孙悟空本身产生影响,但如果变量孙悟空把真身的地址告诉了函数杨戬(地址传递,传递变量孙悟空在内存中的地址),函数杨戬就可以根据地址找到变量孙悟空本身,这时候函数杨戬对变量孙悟空的操作就会改变孙悟空的值了。
引用传递:引用是变量的一个别名,调用别名和调用变量是完全一样的,其效果和地址传递一样。
这里先对值传递进行举例解释,见例1-31。地址传递举例请见1.10.4小节,引用传递举例请见2.2.2小节。
例1-31:值传递。
#include<iostream>
using namespace std;
void swap1(int num1, int num2)//num1和numb2为形参
{
cout<<"swap1中参数交换前: num1= " << num1 <<" num2 ="<<num2<< endl;
int temp = num1;
num1 = num2;
num2 = temp;
cout<<"swap1中参数交换后: num1= " << num1 <<" num2 ="<<num2<< endl;
return ; //当函数声明时,如果函数返回类型为void,则该函数不需要返回值,此处可不写return
}
int main() {
int a = 10;
int b = 20;
swap1(a, b);//调用函数swap(), a和b为实参
cout << "调用swap1交换后main中的 a= " << a<<" b="<< b<< endl;
return 0;
}
例1-31运行结果如图1-50所示。
图1-50 例1-31运行结果
总结:在进行值传递时,形参是改变不了实参(传递前的变量原值)的。
· 1.8.4 函数的常见样式
常见的函数样式有以下4种。
● 无参(数)无返(回值)。
● 有参无返。
● 无参有返。
● 有参有返。
具体见例1-32。
例1-32:函数常见样式。
#include<iostream>
using namespace std;
//函数常见样式
//1.无参无返
void test01()
{
void a = 10; //无类型不可以创建变量,原因是它无法分配内存
cout << "this is test01" << endl;
test01();//函数调用
}
//2.有参无返
void test02(int a)
{
cout << "this is test02" << endl;
cout << "a = " << a << endl;
}
//3.无参有返
int test03()
{
cout << "this is test03 " << endl;
return 10;
}
//4.有参有返
int test04(int a, int b)
{
cout << "this is test04 " << endl;
int sum = a + b;
return sum;
}
· 1.8.5 函数的声明
作用:告诉编译器函数名称和如何调用函数。函数的实际主体可以单独定义。
函数可以声明多次,但是函数只能定义一次。
在函数的声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明。
int max(int, int);
具体声明方法见例1-33。
例1-33:函数的声明。
#include<iostream>
using namespace std;
//声明可以多次,定义只能一次
int max(int a, int b);
int max(int a, int b);
int main() {
int a = 100;
int b = 200;
cout << max(a, b) << endl;
return 0;
}
//定义
int max(int a, int b)
{
return a > b ? a : b;
}
例1-33运行结果如图1-51所示。
图1-51 例1-33运行结果
· 1.8.6 外部文件
作用:调用外部文件可以让代码结构更加清晰,也就是所谓的用分文件保存源文件代码。
函数分文件编写一般有4个步骤。
● 创建扩展名为.h的头文件。
● 创建扩展名为.cpp的源文件。
● 在头文件中写函数的声明。
● 在源文件中写函数的定义。
具体使用方法见例1-34。
例1-34:外部文件。
//swap.h文件
#include<iostream>
using namespace std;
//实现两个数字交换的函数声明
void swap(int a, int b);
//swap.cpp文件
#include "swap.h"
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
//main()函数文件
#include "swap.h"
int main() {
int a = 100;
int b = 200;
swap(a, b);
return 0;
}