C/C++丨函数学习笔记

第五章 函数

本文章内容参考中国铁道出版社《C程序设计(第三版)》
用于记录学习中的一些重要点
欢迎交流学习,有错误请多多包涵,尽管指出

简单函数调用执行过程梗概

first

给形式参数分配内存空间

second

传实型参数(有必要要先计算其表达式)
C语言规定实参到形参为单向传递(若要调整实参变量,涉及到后面的指针传递)
即对形参的改变不会影响实参

对于实参表达式,一般按照从右至左的顺序求值
(总之没事不要瞎折腾那些带副作用的奇怪东西)

third

给函数局部变量分配内存空间

forth

执行函数体

fifth

执行完函数后释放函数调用的内存空间

sixth

(如果有返回值)返回值到函数调用处

p.s.函数说明

此外还有函数声明,常用于调用 执行处后 或 其他程序文件中 定义的函数
值得注意的是,函数不能在别的函数中被定义,但可以在别的函数中被声明

实例

#include <stdio.h>
int main()
{
	double a, b ; 
    double min ( double , double ) ;//函数声明
    printf("Input a,b :") ;
    scanf("%lf%lf" , &a , &b ) ;
    printf("MIN(%f,%f) = %f\n" , a , b , min(a, b) ) ;
    return 0 ;
}
double min ( double x ,double y )
{
    return x < y ? x : y ;
}

函数递归

主要分为:

直接递归调用

不断调用这个函数本身

间接递归调用

两个函数相互交替调用

主要思想

对一个问题进行分解,新问题规模小于原问题,但是解决方法与原问题相同(在这里体现为同一个函数)
可以称之为"递"
最终得到一个最小的规模的解已知的问题
一般称为"递归边界"
最后不断返回
称之为"归"

递归与递推

递归主体是选择结构
递推主题是循环结构

递归分解问题规模直至最基本情况为止,之后通过回溯获得指定规模的解
递推通过不断循环渐进的到达终止条件就结束,没有回溯的过程

存储类别 & 作用域

既然已经讲到了函数的使用,那么我们应该从现在开始了解一些基础的存储相关的知识,以便于理解编写函数时一些需要注意的点

标识符是指常量、变量、语句标号以及用户自定义函数的名称
程序中标识符的进一步的属性:存储类别 、 存储期 、 作用域

1. 存储类别

存储类别用于确定以此标识符明明的对象(例如变量)的 存储期 作用域 连接 etc属性
其中:
存储期 指对象存在的期限
作用域 指该标识符可以在程序中被引用的区域
连接 指该程序由多个源文件组成时,仅标识符存在的源文件可见 还是 适当声明后其他源文件也可见

对于以下四种存储类别关键词:
auto register 用于声明具有自动存储期的变量
extern static 用于声明具有静态存储期的变量和函数 注:这个学问挺深!需多加理解!
注:只有 全局变量和函数名 static声明的局部变量 两种标识符具有静态存储期

  1. 自动的auto
    所有局部变量默认的存储类型,一般省略
    局部变量指函数定义中声明的变量,仅定义函数中可见
    其可以分为 函数定义 or 函数形参 or 复合语句 中的局部变量
  2. 寄存器的register
    表示建议 (因为实际上不一定能够) 让编译系统将改变量装载到计算机的某个高速硬件寄存器中
    适用于 具有自动存储期的 整形or指针类型的 变量
    通常用于频繁使用的计数器等整形变量,以优化对内存的使用
  3. 外部的extern
    全局变量 and 函数名 默认情况为extern存储类型
    全局变量指在函数定义外声明的变量,在整个程序声明周期存在,在程序开始执行时一次性分配和初始化
    使用extern(即默认)的 全局变量 or 函数 可以被其他程序源文件使用
    使用方法见后续拓展
  4. 静态的static
    1. 修饰局部变量时:
      具有静态存储期
      具体来说是第一次运行到此时创建标识定义,然后在整个程序生命周期存在,退出函数时并不同普通局部变量一样经过析构函数销毁
      与全局变量不同的是只能被定义它的函数使用
      示例

      void foo1()
      {
          static int a = 10 ;
          printf("a = %d\n", a ) ;
      
          a -- ;
          return ;
      }
      
      int main()
      {
          printf("请输入输出次数 : n = ") ;
          int n ;
          scanf("%d" , &n ) ;
          for ( int i = 1 ; i <= n ; i ++ )
              foo1() ;
      
          return 0;
      }
      /*请输入输出次数 : n = 5
       *a = 10
       *a = 9
       *a = 8
       *a = 7
       *a = 6
       */
      
    2. 修饰全局变量时:
      表示该全局变量仅当前源程序文件中可见

    3. 修饰函数时:
      表示该函数具有静态存储期的特性,旨在当前源文件的函数中使用,不可在别的源文件中使用

拓展:调用不同源文件中的函数(注意:再次修正时发现此处存在较大的执行性差异,请甄别来看)

在后续学习中,可能会遇见代码量很大的情况,这时我们如果可以将不同作用的函数放在不同的文件中,显然可以大大优化代码的结构

于是我们需要学习一下如何调用不同源文件中的函数
以下介绍一种基础方式即原理,其他的方式可以类推

首先我们有两个源文件:

  1. 函数定义源文件(function.h)
    实际命名成.c后缀也行
    实际上,一般会用function.c的文件中对函数进行定义,而在function.h的文件中进行声明

    #include <stdio.h>
    
    int add ( int a , int b)
    {
        return a + b ;
    }
    
    int a = 10 , b = 20 ;
    
  2. 负责调用的主文件(main.c)
    值得注意的是:头文件处调用要用 “” 而非 <>
    另外从各种层面来讲,最好放在同一个文件目录之中,不确定在不同文件目录是是否可以这样实现调用
    后期修订: 对于上面一行斜体所说的,在学习文件处理技术之后,实际上""中可以是文件的绝对地址(目前我还没明白相对地址怎么用),没有地址默认在同一级文件夹中寻找;相应的,<>则是默认在系统提供的文件中寻找这个头文件库

    #include "function.h"
    
    int main()
    {
        printf("a + b = %d" , add(a , b )) ;
        return 0;
    }
    
存储类别与变量的可见性和存在性
存储类别变量种类可见性存在性默认初值
auto局部 形参定义范围内离开定义范围清除不确定
register局部 形参定义范围内离开定义范围清除不确定
extern全局本文件 其他文件整个程序生命周期0
static局部定义函数中离开定义范围仍保留0
static全局本文件整个程序声明周期0

2. 作用域规则

标志符的作用域:标识符可以被使用的程序段落
标号标识符(?)的作用域是声明标号的函数,该函数内可使用这个标号
此处未能很好理解书本意思,个人理解就是函数内部声明的变量等的作用域就是这个函数
函数外声明的标识符,若为 全局变量 or 函数原型说明 ,其作用域从声明处开始至源程序文件结束
函数 or 复合语句 中声明的标志符,例如
函数形参 作用域为定义形参的函数
函数局部变量 作用域为定义所在的程序块 (也就是从定义处开始到该块的右花括号)
值得注意的是:嵌套语句中,内部和外部标识符同名时,优先可见内部该标识符,外部标识符就等于是不可见了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值