C++函数具有一些C语言不具有的特性,例如内联函数、引用参数、const参数、默认参数、函数重载、函数模板等特性。这些特性将在下一篇介绍。这里先介绍函数的基本知识。
何为函数,编程语言中的函数类似于数学的函数,但又不同于数学中的函数,Wikipedia(function)解释为根据某些规则将一个输入和一个唯一的输出联系起来的关系(a relation that associates an input to a single output according to some rules)。
而在编程语言中,函数毫无疑问是要为程序服务的。而程序的特点就是对特定的输入,能够给出我们想要的输出、完成我们想让它做到的事情。
我对函数的解释是:完成我们想让它做到的特定事件的代码块。
在C/C++中,我们可以通过在定义函数来让它实现我们需要的功能。之后通过调用它来完成我们期望的工作。
函数的使用可以让程序的编写模块化,通过编写特定的程序模块,来简化程序的设计。
使用函数需要提供:
1. 函数定义(实现)
2. 函数原型(声明)
3. 函数调用
函数声明(原型):
type function(parameter_list); //单文件下通常放在main函数前,若将函数定义放在main函数前则无需声明
函数定义的一般格式:
type function(parameter_list) //函数定义
{
do something;
return xxx;
}
函数调用时:
a = function(arguement_list); //a为type类型
例如一个求两个整数中较大的一个数的函数:
int max(int a,int b)
{
return a > b ? a : b;
}
函数返回值可以为void
,即无返回值。可以使用return
不加返回值返回,也可不返回。
有返回值的函数,函数提供一个返回值,使用return
后加返回值实现。返回值可以为任何类型,内置类型或者结构与对象。
parameter_list
为给函数提供的参数列表。包括参数类型和变量名。
在定义函数后必须在main函数前提供函数原型(亦称函数声明),也可以直接在mian函数前直接定义,则不需要函数声明。函数声明:
type function(parameter_list); //函数声明
函数原型可以只提供参数类型,而不必写出参数名。
函数调用时执行函数内部的操作。执行函数的操作之前。会进行参数传递和指令指针(IP)的跳转。通过堆栈实现。
调用函数的表达式的值为返回值。可以将其赋给一个同类型变量。
参数传递有多种方式,C++中有按值传递、指针传递、引用传递。
接受参数的变量称为形参(形式参数),传递给函数的参数为实参(实际参数)。
函数中的变量是私有的,持续时间仅在整个函数运行期间,作用域为变量定义之处到函数结束。函数调用时分配,函数调用结束后释放内存。称为局部变量。
如果参数为普通变量(不是指针、数组或者引用),则参数传递方式为按值传递。
如果是内置类型则按值传递时程序将实参的值复制给形参。如果是程序员自己定义的结构或者类对象按值传递时默认则是每个数据成员逐项复制。其中对象的传递将会调用类的复制构造函数,在没有显示重载构造函数的情况下,将会调用默认复制构造函数,即会逐项复制。关于类和对象的内容会在以后讨论。
函数调用后形参被释放,形参的改变不会影响到原来的实参。
所以下面的函数不能实现两个值的交换:
void swap(int a,int b)
{
int temp = a;
a = b;
b = temp;
}
因为调用时实参复制给形参。调用完成后参数a,b所占的内存空间将被释放。并不会影响到实参。
要实现上述功能,可以采用指针传递:
void swap1(int *a,int *b) //传递的参数必须为指向变量的指针(变量的地址)
{
int temp = *a;
*a = *b;
*b = *temp;
}
或者引用传递:
void swap2(int &a,int &b) //后面介绍
{
int temp = a;
a = b;
b = temp;
}
指针传递原理上来说也是按值传递,即将一个指针变量传递给形参,然后通过指针实现对指针指向变量的操作。
数组名也是地址,所以可以通过传递数组名改变数组元素的值。
函数通过return 返回值;
返回,其中返回值类型必须与函数定义中返回值类型相同,不同时将进行自动类型转换。不能转换时将会报错。
返回值亦可分为按值返回,返回指针与返回引用。
普通类型(内置类型)作为函数参数时,默认传递方式为按值传递。不能通过函数调用改变参数的值。
结构和对象,如果是按值传递,则和普通类型一样。复制时结构和对象内部的数据成员每一项都被复制。
在C++中,结构和对象多用引用传递,引用传递效率更高,可以通过改变形参来改变实参。在不希望参数被改变时,可以使用const
修饰,使其变为常引用,则不可通过函数内部改变变量的值。若此时在函数内部进行了改变函数参数的操作,编译器将会报错。
传递数组时,使用数组名。而数组名是地址,所以数组做为参数是指针传递。所以可以改变数组元素的值。
例如冒泡排序:
void Bubble_Sort(int A[],int n)
{
for(int i=0;ifor(int j=n-1;j>i;j--)
{
if(A[j]1])
swap(A[j],A[j-1]);
}
}
}
void swap(int &x,int &y)
{
int temp = x;
x = y;
y = temp;
}
C风格字符串使用数组存储,所以传递方式同数组。
数组作为参数时形参也可以写作指针,例如将int A[]
替换为int *A
。因为数组名就是一个特殊的指针。
C和C++允许函数调用本身,这种功能称为递归。递归调用通过栈实现。
递归函数必须有结束递归的条件,设计递归函数时必须使其能够结束。否则调用链得不到终止会无限循环下去。
递归函数例:求n的阶乘:
int f(int n)
{
if(n == 0)
return 1;
else
return n*f(n-1);
}
其中递归结束条件为n为0时,返回1,则递归函数将逐级返回,最后返回调用的f(n) = n!。
函数在编译后就是一系列的指令,所以函数也有存放的地址,其地址就是存储其机器代码的内存的开始地址。既然这样,就可以用一个指针变量指向它。从而通过该指针来调用该函数。顾名思义称其为函数指针。
通过函数指针这种技术,通过将一个函数指针作为参数,就可以实现将一个函数传递给另一个函数。这在很多方面有应用,比如类的虚方法的实现等,属于比较深奥的东西。这里简单叙述,待到以后有机会有需求深入接触与学习之后,再详述。