目录
这篇博客记录下C++中的函数的一些概念和用法。
1.函数的概念
C++中特定功能的程序模块是通过函数来实现的,C++中的函数分为标准函数和用户自定义函数。标准函数是系统已经实现并通过函数库形式提供的函数,如数学函数sqrt、pow,字符串处理函数等等。C++中的标准函数都是放在不同的函数库中的,并且有与之对应的头文件。用户自定义函数指的是在开发过程中自己更具实际的开发需要自己定义的函数。这篇主要介绍的是用户自定义函数。
2.函数的定义
函数的定义格式如下:
返回类型 函数名(参数列表){
函数体
}
我们以算符运算符重载为例:
函数名是用户定义的函数标识,一般情况下函数名要反应函数表达的功能。
参数列表是用来说明函数的各个参数,用逗号分开。函数调用的时候,参数的顺序要和参数列表一致。参数列表可以为空,参数列表为空的函数成为无参函数。
返回类型可以是基本数据类型,也可以是构造数据类型。
函数体定义了函数要执行的具体操作。
函数有返回值的时候,使用return关键字+数据类型来实现。对于无参函数,return表示函数终止。
例如我们定义无参函数,用来控制台打印字符串:
void printSomething(string st){
//函数体
}
3.函数调用
函数的调用格式如下:
函数名(实参);
1.语句调用
例如在下面的例子中,我们定义了一个函数swap用来交换外部两个实参的值。
#include <iostream>
using namespace std;
void swap(int & x,int & y){
int z = x;
x = y;
y = z;
}
int main(int argc, const char * argv[]) {
int a = 3,b=4;
cout<<"======>>>函数调用之前<<<======"<<endl;
cout<<"a="<<a<<"\tb="<<b<<endl;
swap(a, b);
cout<<"======>>>函数调用之后<<<======"<<endl;
cout<<"a="<<a<<"\tb="<<b<<endl;
return 0;
}
控制台打印信息如下:
======>>>函数调用之前<<<======
a=3 b=4
======>>>函数调用之后<<<======
a=4 b=3
2.表达式调用
被调用函数做为表达式的一部分。
例如在下面的例子中,max的初始化中调用了maxInt函数。
#include <iostream>
using namespace std;
int maxInt(int a,int b){
if (a >= b) {
return a;
}else{
return b;
}
}
int main(int argc, const char * argv[]) {
int a = 3,b=4;
int max = maxInt(a, b);
cout<<"======>>>max="<<max<<"<<<======"<<endl;
return 0;
}
3.做为参数
我们可以在一个函数中调用另外一个函数,这个其实也很好理解,把这个做为参数的函数想象成一个参数即可。
在下面的例子中,我们可以把参数中的max(a,b)当成一个变量,我们可以把
int result = maxInt(100, maxInt(a, b));
差解乘下面的两个函数:
int c = maxInt(a,b);
int result = maxInt(c,a);
#include <iostream>
using namespace std;
int maxInt(int a,int b){
if (a >= b) {
return a;
}else{
return b;
}
}
int main(int argc, const char * argv[]) {
int a = 3,b=4;
int max = maxInt(a, b);
int result = maxInt(100, maxInt(a, b));
cout<<"======>>>max="<<max<<"<<<======"<<endl;
return 0;
}
4.函数原型
C++中,在使用一个变量之前必须已经定义过这个变量。函数也是一样的,在使用函数之前,必须要定义过这个函数。C++中函数的定义叫做函数声明。
函数声明指的是在函数尚未定义的情况下,事先将函数的有关信息通知编译器,以便编译器能正常进行。
函数在使用之前必须进行声明,这种声明在标准C++语言中成为函数原型。
函数原型的语法如下:
返回类型 函数名(参数列表)
实现函数原型说明有两种形式:
1.第一种方式
直接使用函数定义的头部
#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {//函数实现
float average(int a[10]);
int a[10]={1,2,3,4,5,6,7,8,9,10};
float result = average(a);
cout<<"======>>>result="<<result<<"<<<======"<<endl;
return 0;
}
float average(int a[10]){//函数定义
int sum = 0;
float average;
for (int i = 0; i<10; i++) {
sum = sum + a[i];
}
return sum /10;
}
2.第二种方式
省略参数列表中的形参变量名,只给出函数名、函数类型、参数个数以及顺序。
#include <iostream>
using namespace std;
int add(int,int);
int main(int argc, const char * argv[]) {//函数实现
int a = 3,b=4;
cout<<"a\t+\tb\t=\t"<<add(a, b)<<endl;
return 0;
}
int add(int a,int b){
return a + b;
}
5.函数返回类型
1.有参数有返回值类型
下面是两个参数相加的例子:
#include <iostream>
using namespace std;
int add(int a,int b){
return a + b;
}
int main(int argc, const char * argv[]) {//函数实现
int a = 3,b=4;
cout<<"a\t+\tb\t=\t"<<add(a, b)<<endl;
return 0;
}
2.有参数有返回值类型
下面的例子中我们定义了一个函数,从控制台接收一个参数并且打印它。
#include <iostream>
using namespace std;
void getInt(int a){
cout<<"您输入的整数为:"<<a<<endl;
}
int main(int argc, const char * argv[]) {//函数实现
int a;
cout<<"请输入一个整数:\n"<<endl;
cin>>a;
getInt(a);
return 0;
}
3.无参数有返回值类型
下面的例子中我们定义了一个打印函数。
#include <iostream>
using namespace std;
void printHelloWord(){
cout<<"Hello World!"<<endl;
}
int main(int argc, const char * argv[]) {//函数实现
printHelloWord();
return 0;
}
4.无参数有返回值类型
下面的例子中定义了一个无参数有返回值的函数。
#include <iostream>
#include <string>
using namespace std;
string printHelloWord(){
return "Hello World!";;
}
int main(int argc, const char * argv[]) {//函数实现
cout<<printHelloWord()<<endl;
return 0;
}
6.函数参数
参数是函数之间进行数据交换的主要方式。C++中参数的传递方式有传值和传地址两种方式。此外,C++还提供了默认机制,可以简化复杂的函数调用。
1.参数的传递方式
1.传值
传值是将实参值的副本传递给函数的形参。
函数传值调用的时候,实参和形参是两个不同的变量,有各自的存储空间。优点就是函数调用不会改变函数实参的值。
例如在下面的例子中,我们调用了swap函数,这个函数的作用是交换两个数的值,但是调用函数之后,a和b的值都没变。
#include <iostream>
using namespace std;
void swap(int a,int b){
int c = a;
a = b;
b = c;
}
int main(int argc, const char * argv[]) {//函数实现
int a = 3,b = 4;
cout<<"函数调用之前:a="<<a<<"\tb="<<b<<endl;
swap(a, b);
cout<<"函数调用之后:a="<<a<<"\tb="<<b<<endl;
return 0;
}
2.传地址
我们修改下上个例子的代码,使用指针的方式操作swap函数,调用swap函数之后发现实参的a和b的值被交换了。
#include <iostream>
using namespace std;
void swap(int * a,int * b){
int c = * a;
* a = * b;
* b = c;
}
int main(int argc, const char * argv[]) {//函数实现
int a = 3,b = 4;
cout<<"函数调用之前:a="<<a<<"\tb="<<b<<endl;
swap(&a,&b);
cout<<"函数调用之后:a="<<a<<"\tb="<<b<<endl;
return 0;
}
当然这个例子,我们还可以有更好的代码来实现,我们可以直接修改a和b指向的内存地址来实现。
实现代码如下:
#include <iostream>
using namespace std;
void swap(int & a,int & b){
int c = a;
a = b;
b = c;
}
int main(int argc, const char * argv[]) {//函数实现
int a = 3,b = 4;
cout<<"函数调用之前:a="<<a<<"\tb="<<b<<endl;
swap(a,b);
cout<<"函数调用之后:a="<<a<<"\tb="<<b<<endl;
return 0;
}
2.默认参数
我们在定义函数的时候,给参数赋初始值。
例如在下面的函数定义中,如果参数c的默认值值为0
int max(int a,int b,int c = 0){
//函数体
}
7.函数重载
C++中函数名是唯一的。在函数中相同名称的函数在处理不同的参数做相似的操作,这种情况成为函数的重载。C++会根据类型判断应该调用哪个参数。
例如在下面的例子中,add函数会根据传递的参数是int类型还是float类型调用不同的函数。
最终控制台打印信息如下:
a + b = 7
c + d = 7.3
#include <iostream>
#include <iomanip>
using namespace std;
int add(int a,int b){
return a + b;
}
float add(float a,float b){
return a + b;
}
int main(int argc, const char * argv[]) {//函数实现
int a = 3,b = 4;
cout<<"a\t+\tb\t=\t"<<add(a, b)<<endl;
float c = 3.1,d = 4.2;
cout<<"c\t+\td\t=\t"<<add(c, d)<<endl;
return 0;
}
8.内联函数
内联函数是为了取代预处理函数而引进的。内联函数是通过编译器实现的,是真正的函数,有更高的效率。普通函数调用的时候必须将程序执行的顺序转移到函数所存放的内存中的某个地址,将函数所在的程序内容执行完成之后,在返回到转去执行改函数前的地方。因此普通函数调用有一定的时间和空间方面的开销。而内联函数在编译的时候就已经展开,运行的时候不需要额外的操作。
9.递归函数
函数中自己调用了自己的函数叫做递归函数。
比较好理解的就是阶乘函数。
#include <iostream>
#include <iomanip>
using namespace std;
long fac(int n){
long result;
if( n<0){
cout<<"error"<<endl;
result = -1;
}else if (n==0 || n==1){
result = 1;
}else{
result = n * fac(n - 1);
}
return result;
}
int main(int argc, const char * argv[]) {//函数实现
cout<<"1!="<<fac(1)<<endl;
cout<<"2!="<<fac(2)<<endl;
cout<<"3!="<<fac(3)<<endl;
cout<<"4!="<<fac(4)<<endl;
return 0;
}
10.变量的生存周期
1.静态存储分配
变量有编译程序在编译时给其分配存储空间,并在程序执行过程中始终存在,这类变量包括全局变量、外部静态变量和内部静态变量。这类变量的生存周期与程序的运行周期相同。
自动存储分配,变量由程序在运行时自动给其分配存储空间,这类变量称为函数中定义的自动变量。它们在程序执行到该函数时被创建,在函数执行结束的时候终止。
全局变量全部存储在静态存储区中,在程序开始的时候给全局变量分配存储单元,程序结束的时候释放存储单元的空间。