学习嵌入式的第九天——C语言——函数

函数的认识

如果我们想要实现一台智能ATM机,我们该怎么办?

我们是不是应该从智能ATM机需要什么功能出发,比如说:存钱,取钱,查看余额,修改密码,转账等等。

就以存钱功能来看,当我们想要存钱的时候,是不是需要插入银行卡,并且需要验证这张卡是不是属于我们的,需要输入密码,然后我们就可以把钱放进去,在最后还需要再次确认我们存多少钱。

我们来看,在存钱这个功能下,我们还可以划分出这么多更细的功能。

这就是C语言中的代码模块化,而函数就是实现模块化的好方法。

当我们需要编写C语言来实现一个功能,我们可以对这个功能进行拆分,自顶而下,逐步求精。拆到你可以写出这些小功能,再把这些小功能整合,来实现你需要实现的大功能。

函数设计原则:
   单一职责 
   高内聚 低耦合

函数传递数据:
  全局变量  --- 不建议用,会破坏的c语言结构化编程 
  形参变量  --- 推荐

函数的实现

函数的定义

类型标识符 函数名(形式参数)  //函数头 --head 
{
    //声明部分
    //语句部分
    函数体//body 
    
    return 表达式; //返回函数处理的结果 
}

函数头:
  (1)类型标识符 --- int/short/long ...
                  返回值类型 //说明函数返回的结果的数据类型  
                  注意:
                     1.返回值结果 和 设计的 类型标识符 可能不一致 
                       此时,返回值结果 会被 转换成 类型标识符 指定的类型 
                     2.如果返回值类型省略不写
                       默认 int类型 
                     3.如果不需要带出返回值 
                       void --- 空类型 
                       对应的 return 后面不能有值 
                       return; 
  (2)函数名    --- 标识符 
                  体现函数功能的
                  见名知意 
                  注意:
                    1.形参 与 实参 
                      类型匹配
                      个数相同 
                    2.形参
                      int a,b,c; //普通的定义变量的书写方式 
                      int a,int b,int c //形参变量,每个变量都必须明确的指定类型 
                    3.如果函数功能,不需要传入数据 --- 形参一般设计为 void 

                    4.函数名 --代表 的是 函数的入口地址
                    
                    void menu(void)
                    {
                       printf("------mp3----\n");
                    }
  (3)形式参数  --- 本质是是变量 
                 作用 --用来接受 实际的参数 
                 语法:
                 类型 形参变量名1, 类型 形参变量名2, 类型 形参变量名3,...
函数体:
  //函数对应功能 具体代码

函数定义的位置

C语言中所有 标识符 都必须先定义,后使用  
   main函数是整个程序的入口函数 
   定义的位置
     main之前 
     main之后:
         必须在使用前,做一下函数声明 
         函数声明,是指将函数头复制,然后加上分号 

函数的使用

函数调用:

函数名(实际参数)

函数嵌套调用 
  


   特殊的嵌套调用 --- 自己调用到自己 ,称为递归
   递归:
       1.本质上是个循环 
       2.这种循环 一定会结束 
         因为栈的空间有限 
       3.递归的效率并不高 
         因为有些问题的解决,
         使用递归将变得非常简单 

递归的理解:

递归分为两个过程:递推和回归。

递推过程为,从要解决的问题N往前以N与N-1之间相同的关系推断,直到结束标志,这时在从递推结束的地方不断回归,从一个个的嵌套中返回值,直到返回问题N,这时,就使用递归的方法解决了问题N。

递归解决问题:
需要是一个函数 

1.递推关系 
   问题n 与 问题n-1 之间的递推关系 
2.递归结束的条件 

返回值类型 递归函数(形参 )
{
   if (是否到达结束条件)
   {
      //是 回归
      return 表达式;
   }else 
   {  //假 递推 
      return 问题n和n-1 的 递推结果
   }
}

例如使用递归,求1~100个数的和:

我们需要理解,求1~100的和,是不是求1~99的和再+100,那求1~99的和是不是求出1~98的和再+99,那这样一层层的往下,求1~2的和是不是1+2,1就是等于1,那么随着以这种N与N-1之间的关系不断递推,我们到达了问题N的末尾,这时,我们就开始返回了,把1返回到1+2,把1+2返回到sum(2)+3,我们不断的从一个个嵌套中返回,最后我们返回sum(99)的值到sum(99)+100,这时,我们就得到了1~100的累加和,然后把和返回,函数调用完毕,求1~100的和的问题解决了。

在递归中,最重要的就是每一个问题间的关系,和递推结束的条件了。

如果我们不断的递推,但是没有结束标志,那么就会导致栈溢出,产生段错误。

为什么会产生栈溢出,这就是我们下面要讲的,函数嵌套调用的本质,函数为什么可以嵌套调用。

递归的应用——汉诺塔递归:

1. 将 n-1 个盘子从    A(起始)->B(辅助)
2. 将 剩下的那个盘子从  A(起始)->C(目标)
3. 将 n-1 个盘子从    B(辅助)->C(目标)

 函数嵌套调用的本质 

当我们在一个函数中调用另一个函数,再在这个函数中调用其他的函数,因为每一个函数名都是一个地址,而我们在函数内部编写的代码都存储在这个以地址为开始的空间,所以函数的不断调用其实就是在不同的地址空间中执行程序。那为什么在一个函数调用结束后,系统能准确的找到函数结束后应该继续执行的程序呢?

那是因为系统在内存里开辟了一个专门用来存放相关数据的空间,这个空间具备栈结构的特点,先进后出,所以第一个调用函数的相关数据就存放在栈底,而最后一个调用数据就存放在栈顶,那么当最后一个函数调用结束后,栈顶的数据进行出栈,系统回到最后一个函数调用前的位置继续执行程序,当函数一个个结束,栈中的数据一个个按顺序出栈,由于先进后出,所以系统能准确的找到接下来要执行的程序。栈的开辟无需申请,栈的空间自动释放,所以我们才不需要在程序里对栈进行操作。

但我们能不断的调用函数吗?

当然是不能的,栈开辟在内存里,栈的空间是有限的,Linux系统给每一个栈分配的默认大小是8M(但是栈的大小是可以被改变的)。

数组作为函数参数

数组:
  整型一维
  字符型一维
  整型二维
  字符型二维 
  
int a[10] = {1,2,3,4,5};

a     // --- 整个数组
a[i]  // --- 某个元素 
a[i] //作实参  --- 数组元素做函数实参 --- 和普通变量做函数实参没区别 

注意:
1.整个数组作为函数参数 
从类型角度 
printArray(int a[10]) //
printArray(int *a) //编译器的理解 --- int *a  //指针类型 --- 需要的是一个地址 
                   //从值的角度 --- 数组名代表 首元素的地址 
                   //数组的特点:
                   //  连续性 ,有序性,单一性 
                   // 64位的平台上 ,指针大小都是一样的 8字节                
写法:
   //形参 
   printArray(int a[10],int len);//可以写成这个形式 --- 提高可读性 
   printArray(int *a,int len); //编译器实际理解的形式 

  //实参  --- 数组名 数组长度 
  printArray(a,len);

C语言的程序(5个区):


栈 
  函数调用的相关信息
  局部变量 --- 自动申请,自动释放 
堆 
  空间非常大
  手动申请,手动释放  
全局区/静态区
  放的就是全局变量 静态变量 //默认初始化为 0
字符串常量区
  存放 "hello" 这样的字符串常量 
代码区 
  机器指令 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值