
| 🔭 个人主页: 散峰而望 |
|---|
《C语言:从基础到进阶》《编程工具的下载和使用》《C语言刷题》《算法竞赛从入门到获奖》《人工智能AI学习》《AI Agent》
愿为出海月,不做归山云
🎬博主简介



文章目录
- 前言
- [1. 函数是什么](#1. 函数是什么)
- [2. 函数的分类](#2. 函数的分类)
-
- [2.1 库函数](#2.1 库函数)
-
- [2.1.1 库函数介绍](#2.1.1 库函数介绍)
- [2.1.2 库函数使用举例](#2.1.2 库函数使用举例)
- [2.2 自定义函数](#2.2 自定义函数)
-
- [2.2.1 函数的语法形式](#2.2.1 函数的语法形式)
- [2.2.2 函数定义](#2.2.2 函数定义)
- [3. 函数参数和返回值](#3. 函数参数和返回值)
-
- [3.1 实参和形参](#3.1 实参和形参)
-
- [3.1.1 实参](#3.1.1 实参)
- [3.1.2 形参](#3.1.2 形参)
- [3.1.3 实参和形参的关系](#3.1.3 实参和形参的关系)
- [3.2 函数传参](#3.2 函数传参)
-
- [3.2.1 数组做函数参数](#3.2.1 数组做函数参数)
- [3.2.2 字符串做函数参数](#3.2.2 字符串做函数参数)
- [3.2.3 全局变量不用传参](#3.2.3 全局变量不用传参)
- [3.3 返回值](#3.3 返回值)
- [4. 函数的声明和调用](#4. 函数的声明和调用)
-
- [4.1 函数声明](#4.1 函数声明)
- [4.2 函数调用](#4.2 函数调用)
-
- [4.2.1 传值调用](#4.2.1 传值调用)
- [4.2.2 引用](#4.2.2 引用)
-
- [4.2.2.1 引用概念](#4.2.2.1 引用概念)
- [4.2.2.2 引用特性](#4.2.2.2 引用特性)
- [4.2.3 传址(引用)调用](#4.2.3 传址(引用)调用)
- [4.2.4 传值、传址效率比较](#4.2.4 传值、传址效率比较)
- [5. 函数重载](#5. 函数重载)
-
- [5.1 重载概念](#5.1 重载概念)
- [5.2 重载举例](#5.2 重载举例)
- [6. 练习](#6. 练习)
- 结语
前言
在算法竞赛和程序开发中,函数是构建高效、可维护代码的核心工具之一。通过函数,复杂的逻辑可以被拆解为独立的模块,提升代码的复用性和可读性。C++作为算法竞赛的主流语言,其函数机制不仅支持基础的封装与调用,还提供了诸如函数重载、引用传参等高级特性,帮助开发者优化性能并简化设计。
本文将从函数的基础概念出发,系统介绍C++函数的定义、分类、参数传递、返回值机制以及高级用法。内容涵盖库函数与自定义函数的应用场景、实参与形参的交互原理、传值与传址的效率对比,以及函数重载的实现方式。通过理论解析与代码示例结合,帮助读者掌握函数的核心技术,并能在算法竞赛中灵活运用。
无论是初学者希望夯实基础,还是有经验的选手寻求性能优化,本文均能提供清晰的指导与实践参考。
1. 函数是什么
数学中我们其实就见过函数的概念,比如:一次函数 y = kx + b,k 和 b 都是常数,给一个任意的 x,就得到一个 y 值。其实在 C/C++ 语言中引入了 函数(function) 的概念,有些翻译为:子程序 ,子程序这种翻译更加准确一些。函数就是一个完成某项特定的任务的一小段代码,这段代码是有特殊的写法和调用方法的。
C/C++ 语言程序其实是由若干个小的函数组合而成的,也可以说:一个大的计算任务可以分解成若干个较小的函数(对应较小的任务)完成。同时一个函数如果能完成某项特定任务的话,这个函数也是可以复用的,提升了开发软件的效率。
如果我们要完成这样下面这样的任务:
cpp
#include <iostream>
using namespace std;
int main()
{
int arr[10] = { 0 };
//打印数组的内容
//给数组的元素赋值为1~10
//打印数组
return 0;
}
cpp
//代码1
#include<iostream>
using namespace std;
int main()
{
int arr[10] = { 0 };
//打印数组的内容
int i = 0;
for (i = 0; i < 10; i++)
{
cout << arr[i] << " ";
}
cout << endl;
//给数组的元素赋值为1~10
for (i = 0; i < 10; i++)
{
arr[i] = i + 1;
}
//打印数组
for (i = 0; i < 10; i++)
{
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
可以看到,如果没有函数,我们能写出的代码如上的代码 1,其中有一部分代码是重复的,目的就是打印数组的内容,那如果我们封装成一个函数,在需要的时候调用,就很方便,就是下面的代码 2。
cpp
//代码2
#include<iostream>
using namespace std;
void print_arr(int arr[])
{
int i = 0;
for (i = 0; i < 10; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
int main()
{
int arr[10] = { 0 };
//打印数组的内容
print_arr(arr);
//给数组的元素赋值为1~10
for (int i = 0; i < 10; i++)
{
arr[i] = i + 1;
}
//打印数组
print_arr(arr);
return 0;
}
在代码 2 中 print_arr 就是我们自己定义的一个函数,使用函数能有什么好处呢?
- 模块化开发,一个大的功能,总能拆分成各种子功能,每个子功能都可以设计成一个函数,每个函数可以作为一个独立的模块存在,程序的逻辑更加清晰,逻辑关系更加明确。
- 代码可以复用,只要根据需要定义出一个函数,需要这个功能的地方,直接调用函数就行,降低了代码的冗余,提升了开发效率。
- 方便多个程序员之间协作开发,方便程序的多个模块之间互相交互。
- 熟悉函数的使用之后,代码的编写、阅读、调试、维护都变得更加容易。
2. 函数的分类
在 C/C++ 中,函数一般分为库函数 和自定义函数,那我们接下来就介绍一下。

2.1 库函数
2.1.1 库函数介绍
前面内容中学到的 printf、scanf 都是库函数,库函数是标准库中提供的现成的函数,我们只要学习函数的功能,就能直接使用。有了库函数,一些常⻅的功能就不需要程序员自己实现了,一定程度提升了效率;同时库函数的质量和执行效率上都更有保证。
编译器的标准库 中提供了一系列的库函数,这些库函数根据功能的划分,都在不同的头文件中进行了声明。
库函数的学习和查阅工具很多,比如:
C/C++官方参考手册:https://en.cppreference.com/w/cpp/header.html
C/C++第三方网站:https://legacy.cplusplus.com/reference/(个人更推荐这个)
库函数中有数学相关的,有日期相关的,有算法相关的等等。这些库函数相关的信息都有自己对应的头文件,每一个头文件中都包含了相关的一组函数和类型等信息。
C++ 是兼容 C 语言,所以在 C++ 中也包含了一些来自 C 语言的头文件,这些头文件的后缀是 .h ,如果需要也可以直接包含使用;有一些来自 C 语言的头文件,在 C++ 中会在原来 C 语言的头文件进行了封装,在 C++ 程序中更推荐 C++ 的头文件写法,以下是常见 C 和 C++ 对应的头文件:
| 解释 | C 头文件 | C++ 头文件 |
|---|---|---|
| 常用数学函数 | < math.h > | < cmath > |
| 浮点类型的极限 | < float.h > | < cfloat > |
| C 风格输入输出 | < stdio.h > | < cstdio > |
| 字符串处理 | < string .h > | < string > |
2.1.2 库函数使用举例
举例: sqrt
cpp
double sqrt (double x);

解释:
- sqrt 是函数名
- x 是函数的参数,表示调用 sqrt 函数需要传递一个 double 类型的值
- double 是返回值类型,表示函数计算的结果是 double 类型的值
功能:
Compute square root 计算平方根
Returns the square root of x.(返回平方根)
头文件包括:
库函数是在标准库中对应的头文件中声明的,所以库函数的使用,务必包含对应的头文件,不包含是可能会出现一些问题的。
实践:
cpp
#include <iostream>
#include <cmath> //数学函数头文件,不包含则无法使用sqrt函数
using namespace std;
int main()
{
double d = 16.0;
double r = sqrt(d);
cout << r << endl;
return 0;
}
运行结果:

2.2 自定义函数
了解了库函数,其实库函数的功能是有限的,实际开发过程中还是得根据需要将代码封装成自定义的函数;自定义的函数就是自己设计和实现的函数。
2.2.1 函数的语法形式
其实自定义函数和库函数是一样的,形式如下:
cpp
ret_type fun_name(形式参数)
{
}
语法说明如下:
- ret_type 是用来表示函数计算结果的类型,有时候返回类型可以是 void,表示什么都不返回
- fun_name 是为了方便使用函数;就像人的名字一样,有了名字方便称呼,函数有了名字方便调用,所以函数名尽量要根据函数的功能起的有意义。
- 函数的参数就相当于工厂加工需要的原材料,函数的参数也可以是 void,明确表示函数没有参数。如果有参数,多个参数用逗号隔开,每个参数要分别交代清楚参数的类型和名字。
- { } 括起来的部分被称为函数体,函数体就是完成计算的过程。

我们可以把函数想象成小型的一个加工厂,工厂得输入原材料,经过工厂加工才能生产出产品,那函数也是一样的,函数一般会输入一些值(可以是 0 个,也可以是多个),经过函数内的计算,得出结果。
2.2.2 函数定义
那函数是如何定义的,接下来我们举一个实际的例子,来讲解。
例如:写一个加法函数,完成 2 个整型变量的加法操作。
cpp
#include<iostream>
using namespace std;
int main()
{
int a = 0;
int b = 0;
//输入
cin >> a >> b;
//任务:调用加法函数,完成a和b的相加
//求和的结果放在r中
//to do
//输出
cout << r << endl;
return 0;
}
我们根据要完成的功能,给函数取名:Add,函数 Add 需要接收 2 个整型类型的参数,函数计算的结果也是整型。
所以我们根据上述的分析写出函数:
cpp
#include<iostream>
using namespace std;
//这就是函数的定义
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 0;
int b = 0;
//输入
cin >> a >> b;
//调用加法函数,完成a和b的相加
//求和的结果放在r中
int r = Add(a, b);
//输出
cout << r << endl;
return 0;
}
Add 函数也可以简化为:
cpp
int Add(int x, int y)
{
return x + y;
}

函数的参数部分需要交代清楚:参数个数,每个参数的类型是啥,形参的名字叫啥。
上面只是一个例子,未来我们是根据实际需要来设计函数,函数名、参数、返回类型都是可以灵活变化的。函数的实现就是函数的定义。
3. 函数参数和返回值
3.1 实参和形参
实际上,在函数定义和使用的过程中,函数的参数要被分为两种:
- 实际参数,简称实参
- 形式参数,简称形参
为了更好进行解释上面的概念,先回顾我们前面写的代码:
cpp
#include<iostream>
using namespace std;
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 0;
int b = 0;
cin >> a >> b;
int r = Add(a, b);
cout << r << endl;
return 0;
}
3.1.1 实参
实际参数就是真实传递给函数的参数。
在上面代码中,第 4~9 行是 Add 函数的定义,有了函数后,再第 16 行调用 Add 函数的。我们把第 16 行调用 Add 函数时,传递给函数的参数 a 和 b,称为实际参数 ,简称实参。
3.1.2 形参
在上面代码中,第 4 行定义函数的时候,在函数名 Add 后的括号中写的 x 和 y,称为形式参数 ,简称形参 。
为什么叫形式参数呢?实际上,如果只是定义了 Add 函数,而不去调用的话,Add 函数的参数 x 和 y 只是形式上存在的,不会向内存申请空间,不会真实存在的,所以叫形式参数。形式参数只有在函数被调用的过程中为了存放实参传递过来的值,才向内存申请空间,这个过程就是形式的实例化。
3.1.3 实参和形参的关系
虽然我们提到了实参是传递给形参的,他们之间是有联系的,但是形参和实参各自是独立的内存空间。
这个现象是可以通过调试来观察的。请看下面的代码和调试演示:
cpp
#include<iostream>
using namespace std;
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 0;
int b = 0;
cin >> a >> b;
int r = Add(a, b);
cout << r << endl;
return 0;
}

在调试的可以观察到,x 和 y 确实得到了 a 和 b 的值,但是 x 和 y 的地址和 a 和 b 的地址是不一样的,当 a 和 b 传参给形参 x 和 y 的时候,x 和 y 只是得到了 a 和 b 的值,他们得有自己独立的空间。所以我们可以理解为形参是实参的一份临时拷贝。

3.2 函数传参
3.2.1 数组做函数参数
在使用函数解决问题的时候,难免会将数组作为参数传递给函数,在函数内部对数组进行操作。
比如:写一个函数将一个整型数组的内容,全部置为 1,再写一个函数打印数组的内容。
简单思考一下,基本的形式应该是这样的:
cpp
#inculude <iostream>
using namespace std;
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
set_arr(); //设置数组内容为-1
print_arr(); //打印数组内容
return 0;
}
这里的 set_arr 函数要能够对数组内容进行设置,就得把数组作为参数传递给函数,同时函数内部在设置数组每个元素的时候,也得遍历数组,需要知道数组的元素个数。所以我们需要给 set_arr 传递 2 个参数,一个是数组,另外一个是数组的元素个数。仔细分析 print_arr 也是一样的,只有拿到了数组和元素个数,才能遍历打印数组的每个元素。
cpp
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int sz = sizeof(arr)/sizeof(arr[0]);
set_arr(arr, sz); //设置数组内容为1
print_arr(arr, sz); //打印数组内容
return 0;
}
数组作为参数传递给了 set_arr 和 print_arr 函数了,那这两个函数应该如何设计呢?
这里我们需要知道数组传参的几个重点知识:
- 函数的实参的名字和形参的名字可以相同,也可以不同
- 函数的形式参数要和函数的实参个数匹配
- 函数的实参是数组,形参也写成数组形式的
- 形参如果是一维数组,数组大小可以省略不写
- 形参如果是二维数组,行可以省略,但是列不能省略
- 数组传参,形参是不会创建新的数组的
- 形参操作的数组和实参的数组是同一个数组(因为如果不是同一个数组的话,创建一个新的空间不创建数组,那么数组内部的大小可有可无)
更详细的可以见:
基本魔法语言函数(一)
你真的熟练掌握函数的高阶玩法了吗?
提示:数组传参时候,实参 写数组名就行
根据上述的信息,我们就可以实现这两个函数:
cpp
void set_arr(int arr[], int sz)
{
int i = 0;
for(i = 0; i < sz; i++)
{
arr[i] = 1;
}
}
void print_arr(int arr[], int sz)
{
int i = 0;
for(i = 0; i < sz; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}

3.2.2 字符串做函数参数
那如果将字符串做函数参数呢?其实也很简单,直接在形参的部分使用字符串来接收就可以。这里的形参 s 也是实参 s 的一份临时拷贝,对形参的修改不能影响实参。
cpp
void test(string s)
{
cout << s << endl;
}
int main()
{
string s("hello world");
test(s);
}
3.2.3 全局变量不用传参
全局变量的作用域很大,在整个程序中都可以使用,那么只要把变量、数组等定义成全局变量,在函数中使用,是可以不用传参的,在竞赛中为了方便经常使用,但是在软件工程中很少这么使用。
cpp
#include <iostream>
using namespace std;
int arr[10] = { 0 };
void print_arr()//这里可以不传参
{
int i = 0;
for (i = 0; i < 10; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
int main()
{
//打印数组的内容
print_arr();
//给数组的元素赋值为1~10
for (int i = 0; i < 10; i++)
{
arr[i] = i + 1;
}
//打印数组
print_arr();
return 0;
}
当然,有时候变量或者数组,定义成全局的时候,是不能解决问题,比如:递归等场景,这时候,就得考虑传参的问题。
3.3 返回值
我们在设计的函数的时候,函数在经过计算后,有时候需要带回一些计算好的数据,这时候往往使用 return 来返回,这里我们就讨论一下使用 return 返回。
- return 后边可以是一个数值,也可以是一个表达式,如果是表达式则先执行表达式,再返回表达式的结果。函数返回的值,可以使用变量来接收,如果不需要这个返回值,也可以不接收。
cpp
// 这里使用简化版本的加法函数
int Add(int x, int y)
{
return x + y; // 1. 先执行x+y,得到该表达式计算的结果 2. 执行return,返回结果
}
//在main函数中使用return 0; 返回的就是一个数值
- return 后边也可以什么都没有,直接写
return;这种写法适合函数返回类型是 void 的情况。
cpp
void test(int n)
{
if(n == 2)
return; //只要执行这里的return,函数就提前返回,不再打印数据
cout << n << endl;
}
- return 返回的值和函数返回类型不一致,系统会自动将返回的值的类型隐式转换为函数的返回类型。
cpp
#include <iostream>
using namespace std;
int test()
{
return 3.14;
}
int main()
{
int ret = test();
cout << ret << endl;
return 0;
}
演示结果:

- return 语句执行后,函数就彻底返回,后边的代码不再执行。
cpp
#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;
}
cpp
#include <iostream>
using namespace std;
void print_arr(int arr[], int n)
{
int i = 0;
for(i=0; i<n; i++)
{
if(i == 5)
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;
}
演示结果:


可以看到 return 直接结束函数,而 break 还会打印打印完毕。
即如果函数中 return 语句被执行,则函数直接返回,即使函数中 return 语句后还有其他代码也不再执行。
4. 函数的声明和调用
4.1 函数声明
一般在使用函数的方式写代码的时候,函数定义好之后,在后续的代码中对函数进行调用,例如代码 1。有时候我们也可能将函数定义放在了函数调用的后边,例如代码2,这时候编译器在编译的时候就会有警告出现(如下图),提示这个函数可能不存在,为了消除这种警告,我们一般在函数的调用之前先声明一下这个函数,这就是函数声明。函数声明就是告诉编译器,有一个函数名字叫什么,参数是什么,返回类型是什么,至于这个函数是否真的存在,是要看函数的定义了。函数调用必须满足先声明后使用。

cpp
//代码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;
}
cpp
//代码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;
}
代码 2 的演示结果:

4.2 函数调用
函数调用的方式,一般会分为两类:传值调用和传址(引用)调用 ,我们前面讲过的都是传值调用。因为暂时还没有学习指针,这里下面我们会讲解一下传址调用。
4.2.1 传值调用
案例 1: 写一个函数 Max,求两个整数的较大值。
cpp
#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。
案例 2: 写一个函数 Swap,交换两个整型变量的值。
如果我们按照常规的思路,写出下面的代码。
cpp
#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 函数才能实现交换的效果呢?
这里可以使用指针,也可以使用引用。
4.2.2 引用
4.2.2.1 引用概念
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量是同一块内存空间。
引用使用格式:
类型 & 引用变量名 = 引用实体
cpp
void TestRef()
{
int a = 10;
int& ra = a; // 定义引用类型
//通过printf输出a,ra的地址
printf("%p\n", &a);
printf("%p\n", &ra);
}
4.2.2.2 引用特性
- 引用在定义时必须初始化
- 一个变量可以有多个引用
- 引用一旦引用一个实体,再不能引用其他实体
cpp
#include <iostream>
#include <cstdio>
using namespace std;
void TestRef()
{
int a = 10;
int b = 20;
// int& ra; // 该条语句编译时会出错
int& ra = a;
int& rra = a;
ra = b;//这是赋值不是修改引入
printf("%p %p %p\n", &a, &ra, &rra);
}
int main()
{
TestRef();
return 0;
}
演示结果:

4.2.3 传址(引用)调用
有了前面引用的学习,那就可以利用引用来重新改造前面写的 Swap 函数了。
cpp
#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 >。
cpp
#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;
}
演示结果:

使用 swap 交换两个数组也是可以的:
cpp
#include <iostream>
#include <utility> //swap函数需要
using namespace std;
int main ()
{
int arr1[4];
int arr2[] = {10,20,30,40};
swap(arr1, arr2);
for (int e: arr1)
cout << e << " ";
cout << endl;
return 0;
}
演示结果:

那么字符串能不能使用引用的方式传参呢?当时是可以的。
cpp
#include <iostream>
using namespace std;
void test(string& s)
{
cout << s << endl;
s = "haha";
}
int main()
{
string s("hello world");
test(s);
cout << s << endl;
return 0;
}
演示结果:

4.2.4 传值、传址效率比较
cpp
#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;
}
演示结果:

采用传值调用过程中,函数传参,将实参传递给形参的时候,形参会创建新的空间,再将实参的数据给形参拷贝一份;但是引用传参的方式,就不存在数据的拷贝,只是在形参的部分建立引用的关系,形参就是实参。所以引用传参的效率要高于传值调用的方式。
提示:
数组在传参的时候,形参和实参本来就是同一个数组,所以数组传参的时候,不需要使用引用参数。
5. 函数重载
5.1 重载概念
引入:
比如:我们现在要写一个函数,求两个整数的和,那么我们可以这么写:
cpp
#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 类型浮点数的和,怎么办呢?
cpp
double DoubleAdd(double x, double y)
{
return x + y;
}
那么上面的 IntAdd 和 DoubleAdd 函数的功能是类似的,都是完成求两个数的求和,只是参数类型有差异而已,既然功能是类似的,能不能函数名字统一一下都叫 Add()呢?这样使用 Add() 函数的人,不需要记忆很多名字,就比较方便。这是 C++ 中引入的函数重载的功能。
函数重载: C++ 中的函数重载(Function Overloading)是指在同一个作用域中可以有多个同名函数,它们的函数名称相同,但是参数列表不同。
cpp
函数返回类型 函数名(参数1, 参数2,...);
这里的"不同"指的是参数的数量、类型或顺序至少有一个不同。函数的返回类型并不影响函数的重载,因为 C++ 编译器不会根据返回类型来区分不同的函数.
5.2 重载举例
cpp
#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;
}
6. 练习
结语
函数作为C++程序设计的核心组成部分,在算法竞赛中扮演着关键角色。从基础的函数定义与调用,到参数传递、返回值处理,再到高级特性如函数重载,掌握这些内容能够显著提升代码的模块化程度和复用性。
理解库函数与自定义函数的差异,能够帮助开发者合理利用现有资源,同时灵活扩展功能。参数传递机制(传值、传址、引用)的选择直接影响程序效率和逻辑正确性,尤其在处理大型数据结构时需谨慎权衡。
函数重载通过简化接口设计,让代码更易读且适应多变的需求场景。未来在更复杂的算法实现中,这些基础知识将成为嵌套调用、递归设计乃至面向对象编程的基石。
持续练习函数相关的应用,结合具体算法问题深化理解,是迈向高效竞赛编程的必经之路。
愿诸君能一起共渡重重浪,终见缛彩遥分地,繁光远缀天。
