函数基础
函数的定义
函数由函数名以及一组操作数类型唯一地表示。函数的操作数,也即形参,在一对圆括号中声明,形参与形参之间以逗号分隔。函数执行的运算在一个称为函数体的块语句中定义。每一个函数都有一个相关联的返回类型。
函数的调用
C++ 语言使用调用操作符(即一对圆括号)实现函数的调用。正如其他操作符一样,调用操作符需要操作数并产生一个结果。调用操作符的操作数是函数名和一组(有可能是空的)由逗号分隔的实参。函数调用的结果类型就是函数返回值的类型,该运算的结果本身就是函数的返回值。
函数调用做了两件事情:用对应的实参初始化函数的形参,并将控制权转移给被调用函数。 主调函数的执行被挂起, 被调函数开始执行。 函数的运行以形参的(隐式)定义和初始化开始。
函数体是一个作用域
函数体是一个语句块,定义了函数的具体操作。通常,这个块语句包含在一对花括号中,形成了一个新的作用域。和其他的块语句一样,在函数体中可以定义变量。在函数体内定义的变量只在该函数中才可以访问。这种变量称为局部变量。
当执行到 return 语句时,函数调用结束。被调用的函数完成时,将产生一个在return 语句中指定的结果值。执行 return 语句后,被挂起的主调函数在调用处恢复执行,并将函数的返回值用作求解调用操作符的结果,继续处理在执行调用的语句中所剩余的工作。
形参和实参
类似于局部变量,函数的形参为函数提供了已命名的局部存储空间。它们之间的差别在于形参是在函数的形参表中定义的, 并由调用函数时传递函数的实参初始化。
实参则是一个表达式。它可以是变量或字面值常量,甚至是包含一个或几个操作符的表达式。在调用函数时, 所传递的实参个数必须与函数的形参个数完全相同。与初始化式的类型必须与初始化对象的类型匹配一样, 实参的类型也必须与其对应形参的类型完全匹配:实参必须具有与形参类型相同、或者能隐式转换为形参类型的数据类型。
函数返回类型
函数的返回类型可以是内置类型(如 int 或者 double)、 类类型或复合类型(如int& 或 string*),还可以是 void 类型,表示该函数不返回任何值。
函数不能返回另一个函数或者内置数组类型,但可以返回指向函数的指针,或指向数组元素的指针的指针:
函数必须指定返回类型
// error: missing return type
test(double v1, double v2) { /* ... */ }
早期的 C++ 版本可以接受这样的程序,将 test 函数的返回类型隐式地定义为int 型。但在标准 C++ 中,上述程序则是错误的。
函数形参表
函数形参表可以为空,但不能省略。没有任何形参的函数可以用空形参表或含有单个关键字 void 的形参表来表示。例如,下面关于 process 的声明是等价的:
void process() { /* ... */ } // implicit void parameter list
void process(void){ /* ... */ } // equivalent declaration
形参表由一系列用逗号分隔的参数类型和(可选的)参数名组成。如果两个参数具有相同的类型,则其类型必须重复声明:
int manip(int v1, v2) { /* ... */ } // error
int manip(int v1, int v2) { /* ... */ } // ok
参数表中不能出现同名的参数。类似地,局部于函数的变量也不能使用与函数的任意参数相同的名字。
参数名是可选的,但在函数定义中,通常所有参数都要命名。参数必须在命名后才能使用。
参数类型检查
C++ 是一种静态强类型语句,对于每一次的函数调用,编译时都会检查其实参。
调用函数时,对于每一个实参,其类型都必须与对应的形参类型相同,或具有可被转换为该形参类型的类型。函数的形参表为编译器提供了检查实参需要的类型信息。
调用函数时传递过多的实参、忽略某个实参或者传递错误类型的实参,几乎肯定会导致严重的运行时错误!对于大程序,在编译时检查出这些所谓的接口错误(interface error),将会大大地缩短“编译-调试-测试”的周期。