Menu

C++ 函数:声明、定义与调用(附示例)

如何在 C++ 中编写函数:返回类型、函数名、参数的基本结构,声明与定义的区别,返回值,void 函数,以及漏写 return、忘记函数原型等常见的坑。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

为什么需要函数

函数是一个有名字的代码块,你可以通过调用它,随时让它运行。你不必在好几个地方重复同样的逻辑,而是只写一次,给它起个名字,然后在需要的地方调用这个名字。这能让程序更短、更易读,也更容易修复:在一个地方修改逻辑,每个调用者都会得到更新。

其实你一直都在调用一个函数:main。它是每个 C++ 程序开始运行的入口。现在你要编写自己的函数了。你之前见过的循环,比如基于范围的 for,常常就写在函数里面,这样你就能通过名字复用一整段逻辑。

函数的结构

每个函数都有四个部分:返回类型、函数名、括号中的参数列表,以及花括号中的函数体。

int add(int a, int b) {   // return type | name | parameters
    return a + b;         // body
}
  • int 是返回类型,也就是函数交回的值的种类。
  • add 是你调用它时使用的名字。
  • (int a, int b) 是参数,即由调用者提供的输入。
  • 花括号里是函数体,也就是调用函数时运行的代码。

下面是它在完整程序中的样子。把函数定义写在 main 的上方,main 调用它时就能看到它。

调用 add(2, 3) 时,函数以 a = 2、b = 3 运行,整个表达式就变成了返回的值。你可以把它存进变量,也可以像第二行 cout 那样直接用在另一个表达式中。

返回值

return 语句做两件事:把一个值交还给调用者,并立即结束函数。return 执行之后,它后面的代码都不会再运行,控制权会直接跳回调用函数的地方。

返回值的类型必须与声明的返回类型一致(或能转换成它)。声明为 int 的函数应该返回一个 int;对任何非 void 函数来说,什么都不返回,或者没有 return 就走到了末尾,都是 bug。

void 函数

并不是每个函数都会产生一个值。当一个函数只是做某件事,比如打印输出、更新状态时,它的返回类型就是 void。void 函数可以用单独的 return; 提前退出,也可以一直运行到右花括号。

试图使用 void 函数的结果,比如 int x = greet("Ada");,会导致编译错误,因为根本没有值可以赋给变量。一个常见的错误是在 void 函数里写 return someValue;,编译器同样会拒绝它。

声明与定义

C++ 从上到下读取文件,所以默认情况下,函数必须出现在调用它的代码之前。当这种顺序不方便时,你可以把函数拆成声明(也叫原型)和定义两部分。

声明写出函数的签名,以分号结尾,没有函数体。它向编译器承诺:“这个函数存在,调用方式是这样的。”完整的定义可以放在后面,甚至放在 main 之后。

如果没有第 4 行的原型,编译器会在 main 里先遇到 square(5),而此前它从没见过 square,于是构建就会失败。头文件也正是靠原型,让多个源文件共享同样的函数。注意,声明中的参数名是可选的,写成 int square(int); 也完全可以;对编译器来说,只有类型才重要。

常见错误

有几个陷阱会让新手一次又一次地栽跟头:

  • 先调用、后声明。如果你看到“add was not declared in this scope”这样的错误,说明函数定义在它第一次被调用的位置之后,而且没有原型。把定义移到前面,或者加上原型。
  • 忘记 return。非 void 函数没有执行 return 就到达末尾是未定义行为,调用者会收到垃圾值。编译时打开警告(-Wall),编译器就会把它指出来。
  • 把定义和声明弄混。定义有花括号包起来的函数体,末尾没有分号。声明有分号,没有函数体。把两者搞混,比如在一个本想定义的函数的参数列表后面直接加了分号,就会产生令人困惑的错误。
  • 忽略返回值。单独一行的 add(2, 3); 能通过编译,但算出来的和被悄悄丢掉了。确保你真正用上了函数返回的值。
// Looks like a definition, but the stray ; turns it into a
// declaration followed by a leftover block - a frequent typo:
int triple(int n);   // <- this ; ends the statement
{
    return n * 3;    // n is undefined here; this block is now orphaned
}

下一步:函数参数

你已经看到函数通过参数列表接收输入,但这里面还有很多内容。下一页深入讲解函数参数:值传递与引用传递、默认实参、const 参数,以及这些选择如何决定你的函数能否修改调用者的数据。

常见问题

C++ 中怎么写一个函数?

给它一个返回类型、一个名字、一对放参数的括号,以及用花括号包起来的函数体:int add(int a, int b) { return a + b; }。用名字加实参来调用它,比如 add(2, 3)。如果函数不返回任何值,就用 void 作为返回类型。

C++ 中函数声明和函数定义有什么区别?

声明(也叫原型)告诉编译器函数的名字、返回类型和参数,以分号结尾:int add(int a, int b);。定义还会在花括号中给出函数体。你可以在 main 之前声明一个函数,之后再定义它;有了声明,就能在定义出现之前调用这个函数。

C++ 函数没有返回值会怎样?

对于 void 函数,什么都不会发生,它就这样结束了。但对于非 void 函数,没有执行 return 就到达末尾是未定义行为:调用者会拿到一个垃圾值,程序可能出现异常行为。大多数编译器会对此给出警告;在非 void 函数的每条路径上都一定要返回一个值。

Coddy 编程语言插图

和 Coddy 一起学编程

开始