嵌入式linux学习笔记:03_C语言_函数

本文介绍了嵌入式Linux中C语言的函数概念,包括函数的书写规则、调用方式、返回值、声明与定义的区别,以及从内存角度分析函数运行过程。此外,还讨论了函数嵌套、递归函数、回调函数、变参函数和内联函数的应用与特点。
摘要由CSDN通过智能技术生成

一、函数概念?

1、 在linux C语言中,什么是函数?
C语言是一种面向过程的语言,C语言称之为函数式语言,可以将任何功能都封装成一个函数接口。
2、 在C语言中,封装函数意义?
在某些功能比较复杂,往往用户需要将其封装成一个函数接口,调用者使用该函数时,不需要关注过程,只需要知道传递什么参数和函数有什么返回值即可。

play_music(“xxx.mp3”); -> 参数就是这首歌名字。

听到这首歌 -> 返回值就是听到声音。

二、函数的书写规则?

1、 在linux C语言中,函数有哪些种类?
main函数: 主函数,在C语言中有且仅有一个,一般都在main函数开始执行,然后调用别的函数。

系统函数(system calls):man手册的2手册中,这些函数不需要用户写实现过程。

库函数(library calls):man手册的3手册中,这些函数不需要用户写实现过程。

自定义函数:不能在man手册查询得到,用户必须写清楚实现过程。

例子:

#include <stdio.h>
//1. main函数
int main(int argc,char *argv[])
{
       //2. 调用printf()库函数
       printf("helloworld!\n");
       //3. 调用自定义函数
       func();  //undefined reference to `func'  -> func()实现过程没有定义!  
       return 0;
}

2、自定义函数的书写规则?
1)确定函数的函数名 -> 函数名字最好体现函数的功能
命名规则: 与C语言中变量一样。 例子: my_fun

2)确定函数的形式参数列表
有形式参数:my_fun(形式参数1,形式参数2, … ,形式参数n) -> 有多少个形参,将来就需要传递多少个实参。

无形式参数:my_fun()

什么是形式参数?

形式参数在函数中作用,首先声明好,要调用该函数,首先需要什么参数。

例子: 露营(一张大被子,一张厚席子,一个高枕头,一个绿色桶)

例子:my_fun(int a,int b) -> 说明调用该函数时,需要传递二个整型数据给这个函数!

3)确定返回值类型 -> 写在名字前面
有返回值 -> 确定返回值类型

     int   -> 无论这个函数是调用成功还是失败,都会返回一个int类型。

     char  -> 无论这个函数是调用成功还是失败,都会返回一个char类型。

     float -> 无论这个函数是调用成功还是失败,都会返回一个float类型。
     
例子:
 int my_fun(int a,int b)

  return 0;

无返回值 -> void -> 代表该函数没有返回值

       例子: void my_fun(int a,int b)

  返回值类型 函数名字(形式参数列表)  -> 自定义函数头

4)函数体 -> {}
例子:

int my_fun(int a,int b)

{   -> 功能就写在函数体中

}

3、如何调用函数?
1)确保调用函数实现过程已经写好了!
例子:

int my_fun(int a,int b)
{
       printf("a = %d\n",a);
       printf("b = %d\n",b);
       return 0;

} 

2)直接写函数名字,后面紧跟一个圆括号(),圆括号中存放东西就是实际参数(实参)。
例子:

my_fun(5,10);

结论: 形式参数值由实际参数直接初始化。 -> 这是不可逆的过程。

上述例子: a=5,b=10;

4、函数返回值情况

int my_fun(int a,int b)  -> 需要接受两个参数,返回一个int类型数据。
int ret;
ret = my_fun(5,10);  -> 将my_fun函数的返回值结果赋值给ret。

练习3:写一个函数实现接收三个int参数,经过约定的运算后,得到结果并打印出来。

    前面两个参数是计算,最后参数计算方式。
    计算方式: 
    		 1  -> +
             2  -> -
             3  -> *
             4  -> /

   fun(3,4,2)  -> 结果: 3-4=-1
   fun(3,4,3)  -> 结果: 3*4=12
#include <stdio.h>
int my_fun(int x,int y,int z)  // x=a y=b z=c
{
       int ret;
       switch(z)
       {
              case 1:
                     ret=x+y;
                     break;                 
              case 2:
                     ret=x-y;
                     break;            
              case 3:
                     ret=x*y;
                     break;             
              case 4:
                     ret=x/y;
                     break;             
              default:
                     printf("enter error!\n");
                     break;    
       }
       return ret;
}
 
int main(int argc,char *argv[])
{
       int a,b,c,result;    
       while(1)
       {
              scanf("%d %d %d",&a,&b,&c);
              result = my_fun(a,b,c);
              printf("result = %d\n",result);
       }
       return 0;
}

练习4:以下程序运行结果是?

int fun(int a,int b)
{
       if(a > b)  return a;
       else    return b;
}

int main(int argc,char *argv[])
{
       int x=3,y=8,z=6,r;
       r = fun(fun(x,y),2*z);
       printf("%d\n",r); //12
}

三、函数的声明?

1、 函数的定义与函数的声明有什么区别?
函数的定义包含了函数的名字,返回值类型,形式参数列表,具体实现过程。
例子:

int my_fun(int a)
{
       printf("a = %d\n",a);
       return 0;
}

函数的声明包含了名字,返回值类型,形式参数列表,函数声明必须写在函数调用之前。
用通俗的话来讲,函数的声明意思是: 在你调用某个函数之前你必须要告诉我,这个函数长什么样子。
例子:

int my_fun(int a);

2、 函数声明需要注意的点
1)如果函数定义写在函数调用之前,就不要声明了。
例子:

int my_fun2();  -> 需要声明
int my_fun1()  -> 不需要声明
{

}
int main()
{
      my_fun1();
      my_fun2();
}
int my_fun2()  -> 需要声明
{

}

2)一般库函数/系统函数声明都是放在头文件中
例子:
printf()函数声明写在#include <stdio.h>里面.
所以当我们调用printf()函数时,不需要自己声明,只需要包含对应的头文件即可。

四、从内存的角度分析自定义函数运行过程

1、 回顾栈区以及函数特征
1)函数内部申请的变量都是栈区申请。
2)函数返回时,在函数内部申请过的全部栈区的内存空间都要全部释放。
3)函数返回时,会返回到函数调用的地方。

例题1: 请从内存角度分析以下代码,求出结果?

int swap(int x,int y)
{
       int t;
       t = x;
       x = y;
       y = t;
       return 0;
}

int main(int argc,char *argv[])
{
      int a,b;
       scanf("%d %d",&a,&b);  //5  3
       swap(a,b);
       printf("a = %d,b = %d\n",a,b);  //5  3 
       return 0;
}

例题2: 请从内存角度分析以下代码,求出结果?

int a,b;

int swap()

{

       int t;

       t = a;

       a = b;

       b = t;

       return 0;

}

int main(int argc,char *argv[])
{
       scanf("%d %d",&a,&b);  //5  3
       swap();
       printf("a = %d,b = %d\n",a,b);  //3  5
       return 0;
}

结论:
1)形式参数与实际参数占用不同的储存单位。
2)形式参数值由实际参数直接初始化。

五、函数嵌套?

1、 什么是函数嵌套?
函数嵌套就是调用某个函数内部再调用另外一个函数。

2、 有函数嵌套程序在内存有什么特点?
如果嵌套的函数很多,就会在栈区累积非常多空间没有被释放。

3、 函数嵌套与递归函数有什么区别?
函数嵌套:自己调用别人的函数。

例子:

void fun()
{
       my_fun();
}

递归函数:自己调用自己的函数。

例子:

void fun()

{
       fun();
}

六、递归函数?

1、 特点?
递归函数特点自身调用自身,无限递归,没有终止的时刻。

例子:

void fun()
{
       int a;
       fun();  -> 无限递归,没有终止条件  -> 导致栈空间溢出!
       return;
}

int main()
{
       fun();
       return 0;
}

结论: 为了防止栈空间溢出,所以递归函数一般都会携带终止条件,也就是说到达某个条件时,函数返回!

2、题型1: 给程序,求结果。
例题,求出下面程序的结果?

void fun(int n)
{
       if(n > 1) 
       {
              fun(n-1);
      }
       printf("n = %d\n",n);
       return;
}

int main()
{
       fun(100);
       return 0;
}

答案:1~100
思路: 找出终止条件,列出几项,找规律,再画图分析!

3、 题型2: 给规则,写程序。

例题:写出下列程序。

有5个学生坐在一起,问第5个学生多少岁,他说比第4个学生大2岁。问第4个学生多少岁,他说比第3个学生大2岁。问第3个学生多少岁,他说比第2个学生大2岁。问第2个学生多少岁,他说比第1个学生大2岁。最后问第1个学生多少岁,他说他是10岁,请问第5个同学多少岁?使用递归函数来完成。

#include <stdio.h>
int get_child_age(int n)
{
       int age;
       if(n <= 0)  //  -> 搞事情
              return -1;      
       if(n == 1)  //  -> 终止条件
              age = 10;             
       if(n > 1)   //  -> 正常
              age = get_child_age(n-1) + 2;
    return age;
}

int main(int argc,char *argv[])
{
       int ret;
       ret = get_child_age(5);
       printf("ret = %d\n",ret);
       return 0;
}

思路: 写框架,分析情况 -> 最终目标与终止条件之间的关系。

练习1: 画出以上例子的内存图。

练习2: 有以下程序,求出结果?

int fun(int n)  10   9
{
       if(n==1)  return 1;
       else return (n+fun(n-1));  10+9+8+7+6+5+4+3+2+1
}

int main()
{
       int x;
       scanf("%d",&x);  //若输入一个数字10。
       x = fun(x); 10
       printf("%d\n",x);  //55
}

练习3:第1天存100块,后面每一天都比昨天多存10块,第312天存了多少?

#include <stdio.h> 
int get_money(int n)
{
       int money;
       if(n <= 0)
              return -1;
       if(n == 1)
              money = 100;
       if(n > 1)
              money = 10 + get_money(n-1);
       return money;
}
int main()
{
       int ret;
       ret = get_money(10);
       printf("ret = %d\n",ret);
       return 0;
}

练习4: 使用循环方式完成练习3,画内存图分析两种方式差别。

循环 -> 多次使用一个变量
递归 -> 每次都开辟一个新的变量空间 --> 容易造成栈空间溢出!

七、回调函数

1、 什么是回调函数?
先定义好函数A(喝水),再将这个函数A作为另外一个函数B(爬山)的参数,在函数B(爬山)可以回过头调用这个参数(喝水),这个函数A就称之为回调函数。

2、 基本框架?

void funB(funA)
{
       funA.....  在函数B中调用函数A
}

void funA()  -> 回调函数
{

}

int main()
{
       funB(funA);
}

例题: 从键盘中获取两个数值,求出两个值的和,要求使用回调函数完成。

int get_result(int a,int b,int(*p)(int,int))  -> 就把这个p当成函数名字 
{
       int ret;
       ret = p(a,b);
       return ret;
}

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

int main()

{
       int a,b,ret;
       scanf("%d %d",&a,&b);
       ret = get_result(a,b,add_fun);  -> 函数名字作为参数,其实是传递了函数的地址过来。
       printf("ret = %d\n",ret);

}

补充:
函数指针参数怎么写?
1)先写一个 *
2)在*后面写一个变量名,使用圆括号括住 (*p)
3)确定函数是哪个? int add_fun(int x,int y)
4)把第3步函数名与形参的变量名都去掉 int (int,int)
5)把第2步结果写在第4步结果的返回值类型与形式参数列表之间 int(*p)(int,int)

3、 回调函数使用场景?
在数据库、系统编程信号通信中常常看到回调函数。

练习4: 连续从键盘中获取4个数字,求出其中的最大值,要求回调函数来完成。

int max4(int a,int b,int c,int d,int(*p)(int,int))
{
       int m;
       m = p(a,b);
       m = p(m,c);
       m = p(m,d);
       return m;
}

int max2(int x,int y)
{
       int z;
       z = (x > y ? x : y);
       return z;
}

int main()
{
      int a,b,c,d,ret;
       ret = max4(a,b,c,d,max2);
       return 0;
}

八、变参函数

1、 什么是变参函数?
变参函数是指该函数的参数不固定,例如:printf()函数。

  printf("helloworld!\n");  -> 参数为1
  printf("a = %d\n",a);     -> 参数为2
  printf("(%d %d)\n",x,y);  -> 参数为3

结论: printf()参数为"1+x"

2、如何判断一个函数是不是变参函数?

  #include <stdio.h>
  int printf(const char *format, ...);
   const char *format: 输出字符串格式
   ... :  参数不定

3、 类似的变参函数有很多: printf() scanf() ioctl() fcntl()

九、内联函数?

1、 什么是内联函数?
在程序中调用函数时,需要花费一定的时间进行保护现场与恢复现场,但是使用了内联函数,就既可以使用函数,又不需要花费时间来进行保护现场与恢复现场。

封装:test.c

#include <stdio.h>
void fun()
{
       printf("hello!\n");
}

int main()
{
       fun();
       fun();
       fun();
       return 0;
}

不封装:test.c

#include <stdio.h>
int main()
{
       printf("hello!\n");
       printf("hello!\n");
       printf("hello!\n");
       return 0;
} 

2、 如何实现内联函数?
test.c

int main()
{
       fun();
       fun();
       fun();
       return 0;
} 

head.h

#include <stdio.h>
inline void fun()
{
       printf("hello!\n");
}

3、如何解决保护恢复现场问题?
1)使用内联函数 ->inline
2)不要过多封装成函数,当某些表达式组成一个功能时,才封装为一个函数。

4、在嵌入式中哪里看到内联函数?
在内核链表时看到内联函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值