查看: 124|回复: 0

c++ 函数

[复制链接]

2

主题

6

帖子

9

积分

新手上路

Rank: 1

积分
9
发表于 2023-2-10 13:59:10 | 显示全部楼层 |阅读模式
一、函数的定义
1.1函数定义的一般形式为:

返回类型  函数名(形式参数列表){
    函数声明部分
    函数体执行语句
}
1.2函数名

实现函数需要确定函数名,以便使用函数时能够按名引用
1.3形式参数列表

是函数与调用者进行数据交换的途径,一般形式为:
类型1 参数名1,类型2 参数名2,类型3 参数名3,...
int fun(int x,int y,double m){
    return m>12.5?x:y;
}
函数也可以没有形式参数,定义形式为:
返回类型  函数名(){
    函数声明部分
    函数体执行语句        
}

返回类型  函数名(void){
    函数体声明部分
    函数体执行语句
}
1.4返回类型

返回类型可以是c++除数组外的内置数据类型或自定义类型。
函数可以不返回数据,此时返回类型应写成void,表示没有返回值,其形式为:
void 函数名(形式参数列表){
    函数体声明部分
    函数体执行语句
}
注意:

没有返回值的函数,在调用处是不能按照表达式来调用函数的,只能按照语句形式调用函数,因为函数没有返回值也就不能参与表达式运算。
m=max(a,b);  //以表达式的方式调用max函数
print();  //以语句的形式调用print函数当函数的返回类型不是void,表明函数有返回值。如果函数体内没有return语句,此时函数返回的值是与返回类型形同但内容却是随机的一个值。
1.5函数体

实现函数最重要的是编写函数体。函数体包含声明部分和执行语句,是一组能实现特定功能的语句序列的集合。
编写函数体是为了实现函数功能。故称函数定义为函数实现,简称实现
而函数头称为接口
例子,使用函数实现判断一个数是否为素数。
#include<iostream>
using namespace std;
int IsPrime(int m)//求素数函数
{  //枚举法求m是否为素数
    int i;
    for(i=2;i<=m-1;i++)
        if(m%i==0)return 0;//不是素数返回0
    return 1;//是素数返回1
}
int main(){
    int m;
    cin>>m;
    if (IsPrime(m)) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
    return 0;
}
二、函数的参数

2.1函数参数的作用

是实现函数时的重要内容,是函数接口的首要任务,围绕这个目标需要研究:①②
2.1.1形式参数

①形式参数的定义与实际参数的对应和关系。

形式参数,如
int max(int a,int b){
    return a>b?a:b;
}
第一行a,b就是形参。
形式参数相关说明:

1)函数定义时指定的形参,在未进行函数调用前,并不时间占用内存中的存储单元。
2)只有在发生函数调用时,形参才分配实际的内存单元,接收从主调函数传来的数据。
3)当函数调用结束后,形参占用的内存单元被自动释放。
②函数参数的数据传递机制

包括主要函数与被调函数的双向数据传递。
2.1.2实际参数

函数调用时提供给被调函数的参数称为实际参数
实参必须有确定的值,因为调用函数会将他们传递给形参。实参可以是常量、变量或者表达式,还可以是函数的返回值。
实参相关说明:

1)实参的类型、次序和数目要与形参一致。
2)如果参数数目不一致,则出现编译错误。
3)如果参数次序不一致,则传递到被调函数中的数据就不合逻辑,难有正确的程序结果。
4)如果参数类型不一致,则函数调用时按形参类型隐式类型转换实参。
2.1.3函数参数的传递

2.1.3.1值传递

1、值传递:过程中,形参作为被调函数的内部变量来处理,即开辟内存空间以存放由主调函数复制过来的实参的值,从而成为实参的一个副本。
2、值传递的特点:是被调函数对形参的任何操作都是对内部变量进行,不会影响到主调函数的实参变量的值。
3、值传递时,实参数据传递给形参是单向传递,即只能由实参传递给形参,而不能由形参传回给实参。
三、函数的声明

当要调用函数时,c++规定在调用一个函数之前必须有该函数的声明。
c++规定函数的定义既是函数定义,也是函数声明。换言之,只要函数调用是写在函数定义的后面,就自然有了函数声明。
声明的作用是程序向编译器提供函数的接口信息,因而多次提供接口信息是允许的,但不能提供相互矛盾,语义不一致的接口信息。
3.1函数原型

函数原型的作用是提供函数调用所必须的接口信息,使编译器能够检查函数调用中可能存在的问题,有两种形式:
①返回类型  函数名(类型1 形参1,类型2 形参2,...);
②返回类型  函数名(类型1 ,类型2,.....)
例子
#include<iostream>
using namespace std;
int gcd(int m,int n);//gcd函数原型,gcd函数声明在前
int main(){
    int m,n;
    cin>>m>>n;
    cout<<gcd(m,n)<<endl;//调用时已有gcd函数声明
    return 0;
}
int gcd(int m,int n)//求最大公约数,gcd函数实现后
{
    int r;
    while(n!=0){ //欧几里得算法
        r=m%n;   //r为m/n的余数
        m=n;     //则gcd(m,n)=gcd(n,r)=...
        n=r;     //r=0时即是gcd
}
    return m;
}
'''
m=9,n=6
①r=3,m=6,n=3
②r=0,m=3,n=0
'''
3.2函数调用

1、有了函数声明,就可以调用函数,有参数函数调用的形式为:
函数名(实参列表)
2、实参可以是常量、变量、表达式和函数调用,各参数之间用逗号分隔。实参的类型、次序、个数应与形参一致。
3、c++中,可以用以下几种方式调用函数。
(1)函数表达式:   z=max(x,y)
(2)函数调用语句: max(x,y)
(3)函数实参:  m=max(max(x,y),z)
四、内联函数

1、c++提供一种提高函数效率的方法,即在编译时将被调函数的代码直接嵌入到主调函数中,取消调用这个环节。这种嵌入到主调函数中的函数成为内联函数。
2、内联函数的声明式在函数定义的类型前加上inline修饰符,定义形式为:
inline 返回类型 函数名(形式参数列表)
{
    函数体
}
3、内联函数中不允许使用循环语句和 switch语句
4、内联函数的声明必须出现在内联函数第一次被调用前。
5、内联函数实例:
#include<iostream>
using namespace std;
inline int fun(int a,int b){
    return a*a+b*b;
}
int main(){
    int n=5,m=8,k;
    k=fun(n,m);
    cout<<"k"=<<k<<endl;
    return 0;
}
五、函数的默认参数

c++允许在函数定义或函数声明时,为形参指定默认值,这样的参数称为默认参数,一般形式为:
返回类型 函数名(...,类型 默认参数名=默认值)
{   函数体   }
int add(int x=5,int y=6){
    return x+y;
}
int main(){
    add(10,20);//10+20
    add(10);//10+6
    add();//5+6
    return 0;
}
(1)如果在函数定义时设置了默认参数,那么就不能在函数声明中再次设置,反之亦然。
(2)可以设置多个默认参数设置的顺序自右向左,换言之,要为某个参数设置默认值,则它右边的所有参数必须都是默认参数。
(3)默认值可以是常量、全局变量,甚至是一个函数调用(调用实参必须是常量或全局变量的表达式),不可以是局部变量。
六、函数重载

是在同一个域中用同一个函数名来定义多个函数,但函数参数列表应彼此有不同,或者是参数个数不同,或者是参数类型不同,或者两者均有不同
int add(int a,int b);
int add(double a,double b);  //形参类型不同
int add(int a,int b);
int ass(int a,int b,int c);  //形参个数不同
6.1函数重载的使用说明

(1)重载函数的形参必须不同(个数不同或者类型不同)。
(2)编译程序将根据实参和形参的类型及个数的最佳匹配来选择调用哪个函数。
(3)不要将不同功能的函数声明为重载函数,以免出现调用结果的误解、混淆。
七、函数模板

1、函数模板是一个独立于类型的函数,可作为一种模式,产生函数的特点类型版本。
int abs(int x)    double abs(double x)
使用函数模板可以设计通用型的函数,这些函数与类型无关并且只在需要时自动实例化,从而形成“批量型”的编程方式。
2、函数模板定义的语法类型为:
template<模板形参表>返回类型 函数名(形参列表)
{
    函数体
}

template<typename T>
T abs(T x)
{ return x<0? -x:x;}
3、模板形参表是用一对尖括号<>括起来的一个或多个模板形参的列表,不允许为空,形参之间以逗号分隔,其形式有两种。
①第一种形式
typename 类型参数名1,typename2 类型参数名2,......
②第二种形式
class 类型参数名1,class 类型参数名2,......
4、函数模板举例:
#include<iostream>
using namespace std;
template<class T>
T add(T a,T b){
    return a+b;
}
int main(){
    cout<<"int_add="<<add(10,20)<<endl;
    cout<<"double_add="<<add(10.2,20.5)<<endl;
    cout<<"char_add="<<add('A','\2')<<endl;
    cout<<"int_add="<<add(100,200)<<endl;
    return 0;
}
八、库函数

所谓库函数是指事先由程序员编制好的函数。
(1)在程序中添加库函数声明
多数库函数将自己的函数原型和特殊数据放在头文件(.h)中,所以应首先使用文件包含命令将这些头文件包含到程序中。例如欲使用数学库函数,文件包含命令为: #include<cmath>
从而使得程序有函数声明,例如y=sin(x) 调用就能够通过编译。
(2)将库函数目标代码连接到程序中。
在连接时,例如使用了sin函数,就必须要有sin函数的实现代码才能生成可执行文件,否则连接错误。要将库函数的目标代码能够连接到程序中,主要是配置好开发环境的相关参数,然后由连接器处理。
标准库函数的连接在开发环境中是默认的,一般可以不用特别设置。
九、函数的递归调用

函数直接或间接调用自己称为递归调用
使用函数的递归求n的阶乘的函数:
#include<iostream>
using namespace std;
int fun(int n){
   if(n>1)
       return fun(n-1)*n;
    return 1
}
int main(){
    cout<<fun(5)<<endl;
    return 0;
}
汉诺塔:
#include<iostream>
using namespace std;
void Hanoi(int n,char A,char B,char C){
    if(n==1)
        cout<<A<<"->"<<C<<" ";//只有一个盘子直接A->C
    else{
        Hanoi(n-1,A,C,B);//上面n-1块盘子A->B
        cout<<A<<"->"<<C<<" ";//第n块盘子直接A->C
        Hanoi(n-1,B,A,C);//B塔n-1块盘子B->C
    }
}
int main(void){
    int n;
    cin>>n;
    Hanoi(n,'A','B','C');
    return 0;
}
十、局部变量和全局变量

10.1局部变量

在函数内部或复合语句中(简称区域)定义的变量,称为局部变量,又称为内部变量。
下列变量是局部变量:
①在一个函数内部定义的变量
②函数的形式参数
③在某个复合语句中定义的变量
局部变量的说明:
(1)局部变量只能在定义它的区域及其区域中使用。
(2)在同一个区域中不能定义相同名字的变量。
(3)在不同区域中允许相同名字的变量,但本质上它们是不同的变量。
(4)如果一个变量所处区域的子区域有同名的变量,则该变量在子区域无效,有效的是子区域的变量,称为定义屏蔽。
10.2全局变量

在源文件中,但在函数外部定义的变量,称为全局变量,全局变量的有效区域是从定义变量的位置开始到源文件结束。
函数之间数据传递尽管可以利用全局变量,但这样一来也导致两个函数彼此分不开,违背模块化的原则,所以结构化程序设计提倡少用或不用全局变量
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表