C++函数部分笔记

本文详细介绍了C++中的函数基础,包括函数的定义、调用及返回类型。深入讲解了参数传递的机制,如传值调用与传引用调用的区别,const对形参的影响,以及如何处理数组形参和多实参。同时,讨论了函数指针的声明、使用及其作为形参和返回类型的情况。内容涵盖了函数在实际编程中的多种应用场景和技巧。
摘要由CSDN通过智能技术生成

目录

1.函数基础

2.参数传递

2.1 传值调用跟传引用调用

2.2 const对形参的影响

2.4 指针形参类型

2.5 数组形参

2.6 如何传递多个实参

3.返回类型和return语句

3.1 没有返回值的函数

3.2 有返回值的函数

3.3 返回数组指针

4.函数指针

4.1函数指针的声明

4.2 函数指针作为形参

4.3 函数指针作为返回类型


前言:

C++语法基础部分,主要包括变量与常量,基本数据类型,表达式,语句,函数,类等,而函数部分作为前面几个章节的综合,掌握难度较大,本文旨在通过对该部分的有关概念,用法进行较为完整的梳理,以便巩固所学。


1.函数基础

函数定义的组成:返回类型,函数名,形参列表,函数体

调用运算符:类似和f(),f是函数或者函数指针,f是一个表达式,它的类型就是函数的返回类型

函数调用进行的工作:首先会向被调函数传入实参。其次,程序执行转向被调返回

return语句执行的工作: 返回return语句中的值(如果有)。其次,将程序执行权返回调用该函数的语句所在的函数(主调函数)

函数调用时传递实参的原则:实参数目跟形参相同,两者做到类型匹配(或实参可以转化为形参类型

函数形参列表:可为空,多个形参类型必须分别声明

void fun1() {/.../}

void fun2(voide) {/.../}

void fun3(int a,b) {/.../} //错误

void fun4(int a,int b) {/.../} //正确

函数的返回类型:除了函数跟数组(可以通过对应的函数指针、数组指针间接返回)以外的大部分类型

名字的作用域生命周期:函数体是一个语句块,块内未用static声明的变量叫作局部变量或自动对象,局部变量在定义是创建,程序执行离开块后销毁。而定义在任何函数之外的变量则在程序结束后才会销毁,其生命周期更长。若要延长局部变量的生命周期,可以通过static进行声明为局部静态对象

函数的声明:类似于变量名,函数也要先定义后使用,且函数定义于头文件中,声明于源文件处。函数声明的格式为:

函数返回类型  函数名(形参列表);//形参列表中可以只指明形参类型

分离式编译:随着程序变得越来越复杂,可以将程序分成几个部分,放在不同文件中分别编译,最后再通过链接形成完整的可执行文件。

2.参数传递

在参数传递的部分主要涉及这几个问题:

区分传值调用跟传引用调用;

在形参列表中引入const后的变化;

const对引用形参类型的影响;

指针形参类型;

数组作为形参;

如何向函数传递多个实参;

2.1 传值调用跟传引用调用

传值调用时,是将实参的值拷贝一份,传给形参对应的临时变量中(该变量在函数调用结束后销毁),其作用原理类似于:

int ini_n=0;

int i=n;

i++;

cout<<ini_n<<endl;//输出还是0,ini_n 变量的值不会因为i的改变而受到影响

 传引用调用跟传值调用不同,调用函数时传入的引用实参并不会进行内容拷贝,而是以别名的形式在函数内部使用,若在函数内改变了跟该实参绑定的形参的值,则相当于修该了实参的值。

那么什么时候可以使用传引用调用呢?

首先,但你希望改变实参的内容时;

其次,效率角度考虑,拷贝类类型或容器类型时效率较低,可以通过引用调用

再次,一些类型根本就不支持拷贝,例如I/O类型,只能通过传引用形式调用

最后,由于一个函数虽然可以由多条return语句,但只能执行其中一条,即返回一条信息,如果希望函数返回更多信息,可以通过传入引用实参。

2.2 const对形参的影响

一般变量的初始化规则,可以通过常量或者非常量给声明为顶层const的变量赋予初始值;

const int i=1;

int a=i;

i=2;//错误

顶层const:本身具备常量属性

用实参初始化形参时更上述类似,即使形参被声明为const的,也可传入常量或者非常量对象

2.3 const跟引用形参

当函数形参为如下形式时,形参被声明为const的,并且是底层const(不会修改所引对象的值)

void fun(const int & b)

{

/.../

}

因此,无论传入常量还是变量对象,只要基本数据类型匹配,则都是可以的;

建议在定义函数时尽量使用常量引用,这样普通实参也能调用该函数,反之,若形参是一般的引用,则const版本的实参不能调用该函数。

2.4 指针形参类型

函数指针形参的定义如下所示:

void fun_ptr(int *a){/.../}

void fun_const_ptr(const int *b){/.../}//形参是const的

const对指针形参的影响,跟引用形参类似,这里补充一点,即形参中的const对函数匹配的影响。

函数匹配:编译器根据函数调用传入的实参,从一批同名函数中查找最佳匹配的那个的过程

如下,当函数仅仅是const跟非const时,函数被多次定义,编译器将会报错

int funt(int i){/.../}

int funt(const int i){/.../}

2.5 数组形参

由于数组被允许拷贝,切使用时会被转换为指针,因此将数组作为形参时,本质上是将指向数组首元素的指针作为形参。如下:

int f_arr(const int*);//三种方式均是等价的

int f_arr(const int [ ]);

int f_arr(const int [100]);

把代表数组首地址的指针声明为const说明不能在函数中通过该指针改变数组元素。同理,如果数组形参被定义为引用类型,则可以改变数组元素。

在使用数组的过程中保证范围不越界是一个重要的考虑,一般有三种管理指针形参的方式,以防止指针越界访问:

首先,可以通过标记指针遍历的序列元素,例如C风格字符串,便用‘\0'作为结束标志。

其次,首标准库迭代器的启发,可以通过指向数组首元素,尾后元素的两个指针来访问数组。

最后,可以通过将数组大小作为一个参数传入函数。

当传入的数组是多维数组是,较为重要的一点是,此时指向数组首地址的指针,对应地址里面的元素变成了数组,以二维数组为例,定义如下:

void fun_mul_arr(int  (*matrix)[10] ){/.../}//一个指向数组的指针

void fun_mul_arr(int  matrix[ ][10] ){/.../}//等价定义

2.6 如何传递多个实参

主要有两个方法:使用initializer_list标准库类型(实参数量未知但类型相同),可变参数模板;

在C与C++交互的接口程序中还可使用省略符形参来传递可变数量的实参;

initializer_list类型类似于vector<T>类型,或者说是一个常量数组(元素不可改变),定义在同名头文件中。

initializer_list<string> ls;//元素类型是string

void print_msg(itializer_list<string> ls)//initializer_list类型对象在函数种的使用

{

  for(auto beg=ls.begin(); beg!=ls.end(); beg++ )

        { 

          cout<<*beg<<endl;

        }

           cout<<endl;

}

3.返回类型和return语句

函数可分为有返回值跟无返回值两类

3.1 没有返回值的函数

但函数返回类型为void时,表示函数没有返回值,此时可以在函数体中省略return语句

return 语句有两种形式:

return ;

return expression;

void函数使用第二种形式的返回语句时,表达式必须是另一个返回void的函数。

3.2 有返回值的函数

 对于此类函数首先必须知道这几点:

return 返回的类型必须跟函数定义的返回值类型匹配,或者类型不同但存在类型转化机制;

当函数体有选择控制逻辑时,必须保证任何情况下都能执行到一条return语句

如下所示:

int fun_re(bool a)

{

        if(bool)

         return 0;//错误,当a==false, return 0会被跳过,即导致函数体未执行return语句;

}

3.2.1 值被返回的过程

返回一个值类似于初始化一个变量,函数返回时,返回值会放到一个临时变量中,并将该临时变量作为函数调用的结果

此外,根据前文提到的块内定义的变量是局部变量,若函数返回了一个局部变量的引用或者指针,也将引发错误,因为该引用或指针绑定的对象在函数调用后就被销毁了,引用跟指针将指向无效内存,不可访问!

当函数返回值是类类型时,无论函数调用结果是临时量还是通过引用返回的类对象本身,都可以对调用结果直接使用调用运算符。

auto sz = shorter_string(s1,s2).size();

 同理,当返回类型是可修改的左值(返回一个非常量引用)时,可以通过赋值运算符直接对它赋值。

3.3 返回数组指针

返回数组指针时,有三种方式:类型别名,直接声明,尾置返回

//方式1

using arr_t = int[10];

arr_t * func(int i);

//方式2

int  (*func(int i)) [10];//从里层向外层解读,对一个参数类型是int 的方式解引用,得到一个元                                   //素是int类型的数组

//方式3

auto func(int i) ->int (*)[10]

4.函数指针

函数指针指向的是函数类型,而函数类型由返回类型和形参类型共同决定,声明一个函数指针,只用把返回名替换 为(*ptr)形式,ptr是函数指针的名字。

4.1函数指针的声明

可以通过函数指针进行函数调用,如下所示:

bool len_compare(const string &,cosnt string &){/.../} ;//声明函数类型,定义函数

bool (*pf)(const string &,cosnt string &);//声明函数指针

pf=&len_compare;                           //给函数指针赋值,pf指向了len_compare函数    

bool b1=len_compare("hello","hhhhh");//函数的直接调用

bool b2=*pf("hello","hhhhh");//使用函数指针间接调用len_compare函数

bool b3=pf("hello","hhhhh");//等价调用方式

4.2 函数指针作为形参

4.2.1定义带函数指针参数的函数

void use_bigger(const string s1,const string s2, bool (*pf)(const string &,cosnt string &) );

void use_bigger(const string s1,const string s2, bool  pf(const string &,cosnt string &) );

//两条等价声明

4.2.2使用类型别名简化定义

首先,对函数类型定义别名,如下:

typedef bool Func(const string &,cosnt string &) ;

typedef  decltype(len_compare) Func2;         //等价的别名定义方式

其次,再对函数指针定义别名,如下:

typedef bool (*Func_ptr) (const string &,cosnt string &) ;

typedef  decltype(len_compare) *Func2_ptr; //等价定义方式

简化后的函数声明:

void use_bigger(const string s1,const string s2, Func);

void use_bigger(const string s1,const string s2, Func2_ptr);// 等价声明

4.3 函数指针作为返回类型

最简单的方式是使用类型别名,直接对函数指针类型进行别名,或者对函数类型进行别名通过加上*,再定义函数指针;

using  F =int (int*,int);      //对函数类型进行别名

usint  P_F=int(*)(int*,int);  //对函数指针类型进行别名

//声明返回函数指针类型的函数

P_F f1(int);

F *   f1(int); //等价声明

F     f1(int);//错误,返回了一个函数

此外,还可以直接声明返回函数指针的函数,如下所示:

int (*f1(int)) (int *,int);//左右两侧说明了返回类型是函数指针指向的函数类型

auto f1(int)->int(*)(int*,int); //尾置返回的等价声明

总结,本文首先总结了函数的基础知识,随后从形参列表,形参与实参,返回类型等方面对函数部分内容进行了梳理,同时对函数指针的声明,使用进行了说明。关于函数重载跟默认实参等特殊函数性质本文并未涉及,该部分内容将在后续文章中陈述。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小凡下方了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值