函数与引用

2026-07-14 19:39:19

上一篇我们学了 C++ 的基础语法,所有代码都写在 main 函数里。但真实的程序不可能把所有逻辑塞在一个函数中,我们需要把代码拆分成多个函数,让程序结构更清晰、代码可以复用。

这篇文章除了讲函数的基本用法,还会讲 C++ 中一个非常重要的机制——引用(reference)。学完之后你就能看懂 C++ 代码中函数参数里那些 & 符号了。

¶函数

函数就是一段可以重复调用的代码块,给它一个名字,需要的时候调用就行。

¶定义与调用

一个函数由返回类型、函数名、参数列表和函数体组成:

loading...

return 语句用来返回函数的结果,并且立即结束函数的执行。void 类型的函数没有返回值,不需要写 return(当然你也可以写一个空的 return; 来提前退出函数)。

在 C++ 中,函数必须在调用之前定义。所以在刷算法题时,最简单的做法就是把辅助函数写在 main 函数上面。

¶值传递

C++ 的函数参数默认是值传递,也就是说传进去的是值的副本,在函数内修改参数不会影响外面的变量:

loading...

a 的值传给了 x,但 x 只是 a 的副本,修改 x 不会影响 a。

那如果我确实想在函数里修改外面的变量呢?这就需要用到引用了。

¶引用传递(& 符号)

在参数类型后面加一个 &,就变成了引用传递。引用就是给变量起了个别名,函数内操作的就是原变量本身,而不是副本:

loading...

对比一下:

值传递 void f(int x) —— x 是副本,改了白改

引用传递 void f(int& x) —— x 是原变量的别名,改了就是真改

¶值传递 vs 引用传递:什么时候用哪个

简单记:基本类型传值(副本),容器类型传引用(避免复制)。

基本类型(int、double、bool、char)本身就很小,复制一个 int 几乎没有开销,直接传值就好。除非你确实需要在函数里修改原变量,才用引用。

容器类型(vector、string、map 等)可能包含大量数据,如果传值的话每次调用都要把整个容器复制一遍,非常浪费。所以容器类型一般都传引用。

如果函数只是读取容器、不需要修改它,就加上 const 表示"只读引用":

// 传值:每次调用都复制整个 vector,太浪费了

int sum(vector nums) { ... }

// 引用传递:不复制,但函数内可以修改原容器

int sum(vector& nums) { ... }

// const 引用:不复制,也不允许修改,最安全

int sum(const vector& nums) { ... }

在刷算法题时,最常用的写法就是 const vector& 和 vector&,根据需不需要修改来选择。

¶递归函数传容器的注意事项

这是一个非常容易踩的坑:递归函数中传容器千万别用值传递,否则会超时(TLE)甚至超内存(MLE)。

为什么?因为值传递意味着每次递归调用都会把整个容器复制一份。如果容器有 n 个元素,递归深度是 d,那就会产生 d 份完整拷贝,时间和空间都是灾难。

loading...

上面的回溯算法中,path 用引用传递,所有递归层共享同一个 path,通过 push_back 和 pop_back 来维护状态。如果把 vector& path 改成 vector path(值传递),每层递归都会复制一份 path,不仅浪费内存,而且逻辑上回溯也不对了。

简单记住:递归函数中,容器类型的参数一律用引用 &。

¶函数重载

C++ 允许多个函数同名,只要参数列表不同(参数个数不同,或参数类型不同),编译器会根据调用时传入的参数自动选择正确的版本。这就是函数重载:

loading...

编译器会根据你传入的参数类型和个数,自动匹配最合适的函数版本。函数重载让接口更简洁——同样是"相加"这个操作,你不需要给它们起不同的名字。

¶小结

这篇讲了两个核心内容:

函数让你把代码拆成可复用的小块,避免重复。引用让你在函数中直接操作原变量,避免不必要的复制。

关键点回顾:

值传递是默认行为,传进去的是副本

加 & 变成引用传递,操作的是原变量

基本类型传值,容器类型传引用

const 引用表示只读,既不复制也不修改

递归函数中容器参数一律用引用 &

函数重载:同名函数,参数不同,编译器自动选择

下一篇我们来学 C++ 中常用的数据结构,包括 vector、string、pair 和结构体。

最新发表
友情链接