NO.33十六届蓝桥杯备战|函数|返回值|声明|调用|引用|函数重载(C++)
返回值
我们在设计的函数的时候,函数在经过计算后,有时候需要带回⼀些计算好的数据,这时候往往使⽤return 来返回,这⾥我们就讨论⼀下使⽤ return 返回。
- return 后边可以是⼀个数值,也可以是⼀个表达式,如果是表达式则先执⾏表达式,再返回表达式的结果。函数返回的值,可以使⽤变量来接收,如果不需要这个返回值,也可以不接收。
// 这⾥使⽤简化版本的加法函数
int Add(int x, int y)
{
return x + y;
// 1. 先执⾏x+y,得到该表达式计算的结果 2. 执⾏return,返回结果
}
//在main函数中使⽤return 0; 返回的就是⼀个数值
- return 后边也可以什么都没有,直接写 return; 这种写法适合函数返回类型是 void 的情况
void test(int n)
{
if(n == 2)
return; //只要执⾏这⾥的return,函数就提前返回,不再打印数据
cout << n << endl;
}
- return 返回的值和函数返回类型不⼀致,系统会⾃动将返回的值的类型隐式转换为函数的返回类型
#include <iostream>
using namespace std;
int test()
{
return 3.14;
}
int main()
{
int ret = test();
cout << ret << endl;
return 0;
}
- return 语句执⾏后,函数就彻底返回,后边的代码不再执⾏。
#include <iostream>
using namespace std;
void print_arr(int arr[], int n)
{
int i = 0;
for(i=0; i<n; i++)
{
if(i == 5)
return;//对⽐换成break
cout << arr[i] << " ";
}
cout << endl;
cout << "打印完毕" << endl;
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
print_arr(arr, 10);
return 0;
}
函数的声明和调⽤
函数声明
⼀般在使⽤函数的⽅式写代码的时候,函数定义好之后,在后续的代码中对函数进⾏调⽤,例如 代码1 。有时候我们也可能将函数定义放在了函数调⽤的后边,例如 代码2 ,这时候编译器在编译的时候就会有警告出现(如下图),提⽰这个函数可能不存在,为了消除这种警告,我们⼀般在函数的调⽤之前先声明⼀下这个函数,这就是函数声明。函数声明就是告诉编译器,有⼀个函数名字叫什么,参数是什么,返回类型是什么,⾄于这个函数是否真的存在,是要看函数的定义了。函数调⽤必须满⾜先声明后使⽤。
//代码1
#include <iostream>
using namespace std;
//函数定义
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
int c = Add(a, b);
cout << c << endl;
return 0;
}
//代码2
#include <iostream>
using namespace std;
//函数声明
int Add(int x, int y);
int main()
{
int a = 10;
int b = 20;
int c = Add(a, b);
cout << c << endl;
return 0;
}
int Add(int x, int y) //函数定义
{
return x + y;
}
函数调⽤
函数调⽤的⽅式,⼀般会分为两类:传值调⽤和传址(引⽤)调⽤
传值调⽤
写⼀个函数Max,求两个整数的较⼤值
#include <iostream>
using namespace std;
int Max(int x, int y)
{
return x > y ? x : y;
}
int main()
{
int a = 0;
int b = 0;
cin >> a >> b;
int c = Max(a, b);
cout << c << endl;
return 0;
}
第14⾏调⽤ Max 函数时,就是传值调⽤。传值调⽤就是将实参的数据直接传递给形参。这个过程其实是将实参的值拷⻉⼀份给 Max 函数使⽤,这份副本其实就是形参变量。这时形参和实参是不同的变量,所以对形参的修改,不会影响实参。这种情况下参数传递的⽅式只能从实参到形参,也就是单向传递。为了理解什么是单向传递,我们看⼀下案例2。
写⼀个函数Swap,交换两个整型变量的值。
#include <iostream>
using namespace std;
void Swap(int x, int y)
{
int z = x;
x = y;
y = z;
}
int main()
{
int a = 0;
int b = 0;
cin >> a >> b;
cout << "交换前, a = " << a << " b = " << b << endl;
Swap(a, b);
cout << "交换后, a = " << a << " b = " << b << endl;
return 0;
}
运⾏程序你会发现,其实 Swap 函数中的 x 和 y 确实会交换,但是 main 函数的中的 a 和 b 其实没有交换。这就是值传递的特点,形参和实参是不同的内存空间,对形参的修改不会影响实参,数据仍然是单向传递的,所以交换的效果没有达到。
- 传值调⽤的⽅式在什么时候使⽤?
其实传值调⽤的⽅式⼀般应⽤的场景就是:仅需要通过传参的⽅式将实参的值传递给被调函数,被调函数就可以完成⼯作,⽽不需要改变实参的值。⽐如,案例1中的Max函数,将 a 和 b 的值传递给形参 x 和 y ,在 Max 函数中使⽤ x 和 y 来求较⼤值,就能得到 a 和 b 中的较⼤值。 - 如何改造 Swap 函数才能实现交换的效果呢?
这⾥可以使⽤指针,也可以使⽤引⽤。接下来我们讲⼀下C++中的引⽤。
引⽤
引⽤概念
引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间,它和它引⽤的变量是同⼀块内存空间。
类型&引⽤变量名=引⽤实体;
void TestRef()
{
int a = 10;
int& ra = a; // 定义引⽤类型
//通过printf输出a,ra的地址
printf("%p\n", &a);
printf("%p\n", &ra);
}
引⽤特性
- 引⽤在定义时必须初始化
- ⼀个变量可以有多个引⽤
- 引⽤⼀旦引⽤⼀个实体,再不能引⽤其他实体
void TestRef()
{
int a = 10;
// int& ra; // 该条语句编译时会出错
int& ra = a;
int& rra = a;
printf("%p %p %p\n", &a, &ra, &rra);
}
传址(引⽤)调⽤
#include <iostream>
using namespace std;
void Swap(int& x, int& y)
{
int z = x;
x = y;
y = z;
}
int main()
{
int a = 0;
int b = 0;
cin >> a >> b;
cout << "交换前, a = " << a << " b = "<< b << endl;
Swap(a, b);
cout << "交换后, a = " << a << " b = "<< b << endl;
return 0;
}
上⾯这种实现 Swap 函数实现⽅式就是函数的传引⽤调⽤。这种调⽤⽅式的本质是将实参变量的地址传递给了形参,⽽形参使⽤指针直接找到实参来进⾏操作。所以修改形参的时候,直接改变的就是实参,这就是引⽤的应⽤。
拓展学习:swap函数
其实在C++的STL中也提供了⼀个库函数 swap ,这个函数可以⽤来交换两个变量,也可以交换两个数组(容器的值),如果掌握了,我们就不需要⾃⼰再⾃⼰实现交换的逻辑,直接使⽤现成的函数。
swap 函数需要的头⽂件 <utility>
。
#include <iostream>
#include <utility>
using namespace std;
int main()
{
int a = 0;
int b = 0;
cin >> a >> b;
cout << "交换前, a = " << a << " b = " << b << endl;
swap(a, b);//直接使⽤库函数swap交换两个变量
cout << "交换后, a = " << a << " b = " << b << endl;
return 0;
}
交换数组
#include <iostream>
#include <utility> //swap函数需要
using namespace std;
int main ()
{
int arr1[4]; // arr1: 0 0 0 0
int arr2[] = {10,20,30,40}; // arr1: 0 0 0 0 arr2: 10 20 30 40
swap(arr1, arr2); // arr1: 10 20 30 40 arr2: 0 0 0 0
for (int e: arr1)
cout << e << " ";
cout << endl;
return 0;
}
交换字符串
#include <iostream>
using namespace std;
void printString(string& s)
{
cout << s << endl;
}
int main()
{
string s("hello world");
printString(s);
}
传值、传引⽤效率⽐较
定义了⼀个全局字符串 s ,然后以传值和传引⽤的⽅式进⾏对⽐,看效率的差异
#include<iostream>
#include<ctime>
using namespace std;
//定义全局字符串s
string s("hello world");
void TestFunc1(string s) {}
void TestFunc2(string& s) {}
void Test()
{
// 以值作为函数参数
size_t begin1 = clock();
for (size_t i = 0; i < 10000000; ++i)
{
TestFunc1(s);
}
size_t end1 = clock();
// 以引⽤作为函数参数
size_t begin2 = clock();
for (size_t i = 0; i < 10000000; ++i)
{
TestFunc2(s);
}
size_t end2 = clock();
// 分别计算两个函数运⾏结束后的时间
cout << "TestFunc1(string)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(string&)-time:" << end2 - begin2 << endl;
}
int main()
{
Test();
return 0;
}
采⽤传值调⽤过程中,函数传参,将实参传递给形参的时候,形参会创建新的空间,再将实参的数据给形参拷⻉⼀份;但是引⽤传参的⽅式,就不存在数据的拷⻉,只是在形参的部分建⽴引⽤的关系,形参就是实参。所以引⽤传参的效率要⾼于传值调⽤的⽅式。
数组在传参的时候,形参和实参本来就是同⼀个数组,所以数组传参的时候,不需要使⽤引⽤参数。
函数重载
重载概念
引⼊:
⽐如:我们现在要写⼀个函数,求两个整数的和,那么我们可以这么写
#include <iostream>
using namespace std;
int IntAdd(int x, int y)
{
return x + y;
}
int main()
{
int a = 0;
int b = 0;
cin >> a >> b;
int c = IntAdd(a, b);
cout << c << endl;
return 0;
}
实现⼀个函数求两个 double 类型浮点数的和
double DoubleAdd(double x, double y)
{
return x + y;
}
那么上⾯的 IntAdd 和 DoubleAdd 函数的功能是类似的,都是完成求两个数的求和,只是参数类型有差异⽽已,既然功能是类似的,能不能函数名字统⼀⼀下都叫 Add() 呢?这样使⽤ Add() 函数的⼈,不需要记忆很多名字,就⽐较⽅便。这是C++中引⼊的函数重载的功能
函数重载:
C++中的函数重载(Function Overloading)是指在同⼀个作⽤域中可以有多个同名函数,它们的函数名称相同,但是参数列表不同。
函数返回类型 函数名(参数1, 参数2,...);
这⾥的“不同”指的是参数的数量、类型或顺序⾄少有⼀个不同。函数的返回类型并不影响函数的重载,因为C++编译器不会根据返回类型来区分不同的函数
重载举例
#include<iostream>
using namespace std;
// 1、参数类型不同
int Add(int a, int b)
{
return a + b;
}
double Add(double a, double b)
{
return a + b;
}
// 2、参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
// 3、参数类型顺序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
int main()
{
Add(10, 20);
Add(10.1, 20.2);
f();
f(10);
f(10, 'a');
f('a', 10);
return 0;
}