c++--函数详解篇

  • 一个函数就是一个功能。
  • c++程序一般将一个大的程序划分成若干个程序模块(即程序文件),每个模块实现一个功能。不同的模块可以由不同的人完成。程序进行编译时,以程序文件模块为编译单位,即分别对每一个编译单位进行编译。
  • 一个程序文件中包含若干个函数。无论程序被划分为多少个程序模块,只有一个main函数。程序总是从main函数开始执行,它相当于总调度函数。

函数分类

  • 从用户使用角度:
    1. 系统函数,即库函数。
    2. 用户自己定义的函数。
  • 从函数形式看:
    1. 无参函数
    2. 有参函数

函数的定义

  • 函数是面向对象程序设计中,对功能的抽象
  • 函数声明是函数原型,函数定义是函数功能的确定
  • 定义建立存储空间,声明不建立存储空间
  • 函数定义的语法形式:
    1. 有参函数
      类型标识符  函数名(形式参数表)

    2. 无参函数

       类型名  函数名( )
      {
      	 声明部分
      	 执行部分
      }
      

函数返回值

  • 函数的返回值
    –由 return 语句给出,例如:return 0
    –无返回值的函数(void类型),不必写return语句。

函数参数

  • 用于数据的传递。
  • 定义函数中的参数称为形参,主函数调用一个函数时,函数括号里的参数被称为实参。
  • 形参在未出现函数调用时不会占用存储空间,被称为虚拟参数,表示它并不是实际存在的数据。在函数发生调用时,编译系统会临时开辟空间,形参才被分配内存单元,以便接收从实参传递的数据。调用结束后,形参内存单元会被释放。
  • 实参可以是变量,常量,表达式。
  • 定义函数时,必须指定形参的类型
  • 实参类型必须与实参类型相同。
  • 单项传递,只能实参向形参传递。
  • 用引用做形参
    1. 引用是标识符的别名

      int i, j;
      int &ri = i;
      //建立一个int型的引用ri,并将其
      //初始化为变量i的一个别名
      j = 10;
      ri = j;//相当于 i = j;

    2. 声明一个引用时,必须同时对其进行初始化,使它指向一个已存在的对象。

    3. 一旦一个引用被初始化后,就不能改为指向其它对象。

    4. 引用可以作为形参
      void swap(int &a, int &b) {…}

函数调用

  • 调用前先声明函数

    按如下形式声明函数原型:

    类型标识符 被调用函数名 (含类型说明的形参列表);
    –若函数定义在调用点之前,则无需另外声明;
    #include  <iostream>
    using namespace std;
    double  power(double x, int n) {
    	double sum = 1;
    	for (int i = n; i <= 1; i--) {
    		sum *= x;
    	}
    	return sum;
    }
    void  main() {
    	double sum = power(4, 4);
    	cout << sum;
    }
    

    –若函数定义在调用点之后,则需要在调用函数前

    > #include <iostream>
    using namespace std;
    double power(double x, int n);
    int main() {
    double result= power(5, 2) ;
    cout << result<< endl;
    return 0;
    }
    //计算x的n次方
    double power(double x, int n)
    {
    double val = 1.0;
    for(int i=1; i<=n;i++)
    val *= x;
    return val;
    }
    
    
  • 函数调用方法:

    1. 函数语句
      把函数调用单独作为一个语句。

      print();

      扫描二维码关注公众号,回复: 3503006 查看本文章
    2. 函数表达式
      函数出现在一个表达式中,要求函数带回一个确定的值。

      int c=2*max(a,b);//a,b是一个确定的值

    3. 函数参数
      函数作为另一个函数的参数。

      m=max(a,sqrt(b));

  • 嵌套调用

–函数可以嵌套调用,但不允许嵌套定义。

  • 递归调用

–函数直接或间接调用自身。

int f(int x){
 int y,z;
 z=f(y);
 return z;
}

内联函数(内置函数)

  1. 声明时使用关键字 inline。
  2. 编译时在调用处的语句用函数体进行替换,节省了参数传递、控制转移等开销,即在编译时将整个代码块直接嵌入主函数中,而不是将流程转出去。
  3. 注意:
    –内联函数体内不能有循环语句和switch语句。
    –内联函数的声明必须出现在内联函数第一次被调用之前。
    –对内联函数不能进行异常接口声明。

带缺省形参值的函数

  1. 函数在声明时可以预先给出缺省的形参值,调用时如给出实参,则采用实参值,否则采用预先给出的缺省形参值。

    例如:
    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
    }
    
  2. 有缺省参数的形参必须在形参列表的最后,也就是说缺省形参值的右面不能有无缺省值的参数。因为调用时实参与形参的结合是从左向右的顺序。

    例:
    int add(int x, int y = 5, int z = 6);//正确
    int add(int x = 1, int y = 5, int z);//错误
    int add(int x = 1, int y, int z = 6);//错误

  3. 如果一个函数有原型声明,且原型声明在定义之前,则缺省形参值必须在函数原型声明中给出;而如果只有函数的定义,或函数定义在前,则缺省形参值需在函数定义中给出。

    例:
    	1. //原型声明在前
    int add(int x = 5,int y = 6);
    int main() {
     	add();
    }
    int add(int x,int y) {//此处不能再指定缺省值
      return x + y;
    }
       2. //只有定义,没有原型声明
    int add(int x = 5,int y = 6) {
      return  x + y;
    }
    

函数重载

  1. C++允许功能相近的函数在相同的作用域内以相同函数名声明,从而形成重载。方便使用,便于记忆。

例:
形参类型不同
int add(int x, int y);
float add(float x, float y);
形参个数不同
int add(int x, int y);
int add(int x, int y, int z);

  1. 重载函数的形参必须不同:个数不同或类型不同。
    编译程序将根据实参和形参的类型及个数的最佳匹配来选择调用哪一个函数。
    在这里插入图片描述

函数模板

  1. 建立一个通用函数,其函数类型和形参类型不具体指明,用一个虚拟的类型来代表。
  2. 凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中定义一次即可。
  3. 定义函数模板的一般形式:
     template \<typename T>
              or
     template \<class T>
  1. 适用于函数体相同,函数的参数个数相同而类型不同的情况。

在这里插入图片描述

作用域:局部变量和全局变量(外部变量)

  1. 在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。
  2. 全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序。
  3. 当全局变量与局部变量同名时:
    在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。
  • 局部变量:

    1. 自动变量(动态局部变量):离开函数,值消失
    2. 局部变量:离开函数,值保留
    3. 寄存器变量:离开函数,值消失
    4. 形式参数:可以是自动变量或寄存器变量
  • 全局变量:

    程序执行全过程都占用存储单元

    1. 静态外部变量:static修饰,只能本文件使用
    2. 外部变量:非Static修饰的外部变量,可被其他文件引用
  • 四种作用域:

    1. 文件作用域(全局的)
    2. 函数作用域
    3. 块作用域
    4. 函数原型作用域

存储期: 动态存储和静态存储

  • 内存中存储空间分为:

    1. 程序区
    2. 静态存储区
    3. 动态存储区

    程序中所使用数据分别存放在静态和动态存储区。

  • 存储期:变量在内存中的存在周期

    1. 静态存储
      -在程序运行过程中,系统对变量分配固定的存储空间。
      -存放:全局变量
      -程序开始执行时就给全局变量分配存储单元,执行结束释放。
      -自动给变量赋初值,默认为0

    2. 动态存储
      -在程序运行过程中,系统对变量动态分配存储空间。
      -存放:

      1. 函数形式参数:调用函数时给形参分配存储空间
      2. 函数中定义的变量:未加static修饰的局部变量
      3. 函数调用时的现场保护和返回地址

      -函数调用时分配动态空间,调用结束时释放空间

4种常见存储类别

  • 自动的 --auto

    1. 动态局部变量–动态存储
    2. 函数中定于的变量都属于自动变量,可以省略auto关键字
    3. 函数调用时赋初值,每次重新调用需要重新赋初值。
    4. 若不赋值,是一个不确定的值。
  • 静态的–static

    1. 静态局部变量
      1. 静态局部变量在静态存储区内分配存储单元。
      2. 编译时赋初值的,只赋初值一次。程序运行时已经有初值。以后调用都不需要赋初值,保留上次调用函数时的值。
      3. 若定义时未赋初值,编译时自动赋初值0或空字符。
    2. 静态外部变量
      1. 只允许外部变量被本文件引用,不被其他文件引用。
  • 寄存器的–register

    1. 变量的值一般存放在内存中。当需要某个变量的值,控制器发出指令将内存中该变量的值送到cpu中的运算器。再送回内存。
    2. 某变量频繁使用,为提高执行效率,可以将其声明为register,存放在寄存器中,提高存取速度。
  • 外部的–extern

  1. 单文件内声明全局变量
    若想在定义点之前想引用某全局变量,需要用extern对该变量做外部申明。

    #include <iostream>
    using namespace std;
    int main(){
    extern int a,b;//对全局变量a,b作提前引用声明;
    cout<<a<<b;
    return 0;
    }
    int a=15;//定义全局变量
    int b=-1;//定义全局变量

  2. 多文件的程序中声明外部变量
    在一个源文件中引用另一个文件中已定义的外部变量:

    1. 在一个文件中定义外部变量;
    2. 在另一个文件中用extern对变量做外部变量声明;

    extern只是变量声明,不是变量定义。他只是扩大已定义变量作用域。

    file1.cpp
    extern int a,b;
    int main(){
    cout<<a+b;
    }

    file2.cpp
    int a=2,b=5;

    在编译连接成一个程序后,file2.cpp中的a和b作用域扩展到file1中

内部函数和外部函数

  • 内部函数(静态函数)

    函数只能被本文件夹中其他函数调用
    格式:static 类型标识符 函数名 (形参列表)
  • 外部函数

    可被其他文件引用
    格式:extern 类型标识符 函数名 (形参列表)

c++系统函数

  1. C++的系统库中提供了几百个函数可供程序员使用。

例如:求平方根函数(sprt)、求绝对值函数(abs)等。

  1. 使用系统函数时要包含相应的头文件。

例如: cmath 或 math.h

    • 标准C++函数
      –C++标准中规定的函数;
      –各种编译环境普遍支持,因此用标准函数的程序移植性好;
      –很多标准C++函数继承自标准C,头文件以c头: cmath, cstdlib, cstdio, ctime……
    • 非标准C++函数
      –与特定操作系统或编译环境相关;
      –在处理和操作系统相关事务时常常需要调用。

形参和局部变量的存储

  1. 为什么不能为形参和局部变量分配固定地址?
    –他们仅在函数调用时生效,函数返回后即失效,分配固定地址造成空间浪费
    –更重要的是,发生递归调用时,多次调用间的形参和局部变量应彼此独立

  2. 需要栈式存储

  • 栈是一种容纳数据的容器
    1. 数据只能从栈的一端存入(压入栈)
    2. 数据只能从栈的同一端取出(弹出栈)
  • 运行栈
    1. 运行栈是一段区域的内存空间
    2. 运行栈分为一个一个栈帧
      –每个栈帧对应一次函数调用
      –栈帧中包括:
      • 本次函数调用的形参值
      • 控制信息
      • 局部变量值
      • 一些临时数据
    3. 函数调用时,会一个栈帧被压入运行栈
    4. 返回时,会有一个栈帧被弹出

    在这里插入图片描述

函数调用的执行过程

在转向被调用函数之前,要记下当时执行的指令的地址,还要保护现场,以便函数调用之后返回现场继续执行。
在这里插入图片描述

头文件

  • c++编译系统保留了c的头文件
  • 头文件把不同文件组合在一起,形成一个文件。
  • 头文件是源文件之间的接口
  • 头文件包含内容:
    1. 对类型的声明
    2. 函数声明
    3. 内置函数声明
    4. 宏定义·
    5. 全局变量定义
    6. 外部变量声明
    7. 其他头文件
  • c++提供两种不同形式的头文件

#include <math.h> c形式
#include <cmath> c++形式

猜你喜欢

转载自blog.csdn.net/qq_41498261/article/details/82903274