嵌入式-C语言-5-函数

一、函数(核心)


1.1.明确:

任何C程序,C源文件都包含两部分内容:一对的变量(包括数组)和一堆的函数


1.2.函数概念:

函数就是一堆语句的组合,用以实现一些相对独立并且具有一定通用性的功能
分析:没有函数的代码极其繁琐啰嗦,代码重复重复的编写,加大开发的工作量期望:只需将以上重复的代码写一遍即可,其他文件只需使用即可,减少开发的工作量
    方案:
     vim add.c

//编写一个加法函数add
     int add(int x, int y)
     {
    if(x < 0 || y < 0) {
        printf("请重新输入正数.\n");
        return -1; //让程序结束,不能继续运行
    }
    //只有输入的数是正数才能加
    sum = a + b;
    printf("sum = %d\n", sum);
    return sum;
     }

    vim main1.c

 int main(void)
      {
    int a;
    int b;
    int sum = 0;
    scanf("%d%d", &a, &b);
    sum = add(a, b);
    return 0;
      }

     vim main2.c
   

  int main(void)
      {
    int a;
    int b;
    int sum = 0;
    scanf("%d%d", &a, &b);
    sum = add(a, b);
    return 0;
      }

     vim main3.c ....main250.c 只需很轻松的用add函数即可
    
1.3.函数特点:
      1.由一条或者多条语句组成
      2.可以重复使用

1.4.函数使用三步骤:
a)函数声明:
   1.函数声明的功能:告诉编译器,将来这个函数可以给别人或者自己使用,函数声明是不分配内存空间的
   2.函数声明的语法:extern 返回值数据类型  函数名(形参表);
      建议:函数声明加extern(提高代码的可读性),理论上可以不加 
   3.函数声明特点:
      1.如果函数定义在函数调用之前,可以省略函数声明,否则必须声明
      2.该声明的没有声明,那么编译器gcc就会给个默认的函数什么,,形式:
         extern int 函数名(); 
      3.函数名的命名遵循标识符的命名规则

b)函数定义:
   1.函数定义功能:就是一个函数的具体实现过程,里面会包含一堆的语句,将来可以给别人或者自己使用,函数定义会分配内存
   2.函数定义的语法:
      返回值数据类型  函数名(形参表)
      {
    一堆的函数体语句;
      } //花括号的作用就是圈语句用
     例如:

 int  add(int x, int y) //x=100,y=200
     {
    return x + y;
     }
     x = 250; //报错
     y = 251; //报错
     
     int main(void)
     {
    sum = add(100, 200) = 300; //main函数使用add函数
    return 0;
     }


  1.5 函数定义特点:


     1.返回值数据类型:就是函数运行完毕要给使用这个函数的代码返回一个数,那么这个数必然有对应的数据类型,如果函数没有返回值,返回值数据类型用void关键字。


     2.形参表:就是一堆的变量定义,这些变量只能在这个函数体内部使用,出了函数就不能用,形参的值由使用这些函数的代码来赋值,形参的变量有多个,用逗号,分开,如果使用函数的代码不想给函数传递参数,函数定义的时候形参表写void
         建议:形参表变量定义的个数不要超过4个,要小于等于4个,否则影响函数使用效率
         例如:
         int add(int x, int y, int z, int m) {} //好
         int add(int x, int y, int z, int m, int n) {} //代码的执行效率降低


     3.函数的返回值:
        如果函数需要返回一个数字,用关键字return,例如:return 返回值
        切记:如果忘记了写return 返回值,gcc将来会返回一个随机数
        注意:返回值的数字的类型和函数定义时的返回值数据类型要一致,如果不一致,会进行隐式转换,可能造成数据的丢失
        例如:

int add(int x, int y)
     {
        int sum = x + y;
        return sum;
    }


       如果函数没有返回值,可以不用写return或者写return后面不加内容!
       例如:

void add(int x, int y)
    {
        printf("%d\n", x+y);
        return; //或者不写
    }

c)函数调用:
   1.函数调用功能:俗称使用函数,调用函数,访问函数
   2.函数调用语法:接收函数返回值的变量 = 函数名(实参表);
   3.函数调用特点:
      实参表:就是给函数的形参表赋的值
      例如: sum = add(100, 200);//100,200就是实参,将来给add函数的x,y
      实参的内存空间和形参的内存空间是独立的,地址是不一样的,但是里面存储的数据是一样的!

d)函数使用的形式:
形式1:无参无返回值
          例如: void 函数名(void) 
        {
             函数语句;
        }
形式2:无参有返回值
         例如:int 函数名(void)
       {
             函数语句;
       }
形式3:有参(1个形参)无返回值
         例如:void 函数名(int x)
       {
             函数语句;
       }    
形式4:有参(多个形参,建议小于等于4个)无返回值
         例如:void 函数名(int x, int y, int m, int n)
       {
             函数语句;
       }
形式5:有参有返回值
         例如:int 函数名(int x, int y, int m, int n)    
       {
             函数语句;
       }
形式6:参数不定有返回值(不建议这么做)
         例如:int 函数名()    
       {
             函数语句;
       }

e)return关键字和exit函数
   return关键字:实现函数返回
   exit函数:让程序强制结束,为了使用此函数需要添加头文件:#include <stdlib.h>

f)实参和形参
   形参就是实参的一份拷贝,实参给形参传递参数时,就是将实参里面的数字拷贝一份给形参

1.5.函数和数组
a)之前的代码都是研究函数如何操作变量,也就是函数如何操作通过变量分配的内存
b)函数访问数组编程公式
   /*定义访问数组的函数*/
  void 函数名(数组的首地址, 数组的长度)
  {

         可以查看数组元素

         可以修改数组元素

  }

  例如:

  void array_function(int a[], int lenght)

  {

//查看数组元素的值

printf("%d\n", a[下标]);

//修改数组元素的值

a[下标] = 新值;

  }

  形参:int a[]本质就是一个数组的首地址,而不是数组,因为这么定义数组语法是不通过的,函数通过这个地址访问元素时,可以当成数组来用

明确:不能用变量,因为如果是变量,形参是实参的拷贝,只能改变形参改变不了实参,要想改变,目前只能用函数和数组技巧

2.作用域和可见性

2.1.明确:C语言变量按照数据类型(占内存大小)分:12种(char....double)

2.2.明确:C语言变量按照作用域和可见性分两类:局部变量和全局变量

2.3.局部变量定义:定义在函数内部的变量

      例如:

      void A (void) 

      {

int a = 250; //局部变量

      }

2.4.全局变量定义:定义在函数之外的变量

     例如:

      int g_b = 520; //全局变量

      void A (void) 

      {

int a = 250; //局部变量

      }

      int g_c = 521; //全局变量

2.5.static关键字

      如果定义变量时前面加static关键字修饰,表示此变量为静态变量

      如果定义变量是前面没有加static关键字修改,表示此变量为非静态变量

      例如:

      int a = 250; //非静态变量

      static int a = 250; //静态变量

2.6.结论:最终C语言变量按照作用域和可见性来分,最终分四大类:

      局部非静态变量/局部静态变量/全局非静态变量/全局静态变量

2.7.详解局部非静态变量特点:

a)形式1:

   void A(void)
   {
printf("a = %d\n", a); //不可以,报错,gcc编译时报没有定义的错误
int a = 250; //定义局部非静态变量
printf("a = %d\n", a); //可以
   }

b)形式2:  

 void A(void)
   {
if(1) {
     int a = 250;
     printf("a = %d\n", a); //可以
}
printf("a = %d\n", a); //不可以,报错,gcc编译时报没有定义的错误
   }

c)函数的形参

   void A(int a) //a在整个函数体内都可以用,出了函数不能用

d)局部非静态变量特点

   1.此变量使用范围:从定义的地方开始依次向下直到最近的花括号结束

                注意:if...else/switch...case/for/while/do-while

   2.此变量分配的内存生命周期:从定义的地方操作系统就会给变量分配内存,直到最近的花括号操作系统立马收回变量的内存,只能等下一次运行这些代码才能给变量重新分配内存

2.8.详解局部静态变量特点:

a)形式1: 

  void A(void)
   {
         printf("a = %d\n",a);//不可以,报错,gcc编译时报没有定义的错误
         static int a = 250; //局部静态变量
         printf("a = %d\n",a); //可以
   }

b)形式2:

   void A(void)
   {
if(1) {
    static int a = 250; //局部静态变量
     printf("a = %d\n",a); //可以
}
   printf("a = %d\n",a);//不可以,报错,gcc编译时报没有定义的错误
   }

c)局部静态变量特点:

   1.此变量使用范围:从定义的地方开始到最近的花括号结束

   2.此变量分配的内存生命周期:从定义的地方开始分配内存直到程序结束,一次分配,程序不结束,下次不会分配,接着上一次的使用,程序不结束,下次就不会重新分配内存

   3.由衷建议:少用此类变量

2.9.详解全局非静态变量:   

a)形式1   

 void B(void)
    {
printf("g_a = %d\n", g_a); //不可以,gcc报没有定义的错误
    }
    int  g_a = 10; //定义初始化全局非静态变量
   void A(void)
   {
printf("g_a = %d\n", g_a); //可以,函数内部访问全局非静态变量
   }
   void C(void)
   {
printf("g_a = %d\n", g_a); //可以,函数内部访问全局非静态变量
   }
   ...

b)形式2:全局非静态变量定义和使用在不同的源文件中进行

   切记:如果一个源文件定了一个全局非静态变量,另一个源文件想直接访问这个全局非静态变量,只需声明这个全局非静态变量即可

             声明全局非静态变量的语法:extern 数据类型  全局非静态变量名;

             结论:一旦声明,就可以访问别的文件定义好的全局非静态变量了

  vim var1.c 添加如下内容

  int g_a = 250; //定义全局非静态变量
 //定义A函数
  void A(void)
  {
printf("A函数:g_a = %d\n", g_a); //查看打印全局非静态变量
  }
//定义B函数
  void B(void)
  {
g_a = 520; //修改全局非静态变量
  }

  vim var2.c 添加如下内容  

/*定义D函数*/
  void D(void)
  {
printf("%d\n", g_a); //gcc编译报错,没有定义
  }
  //由于跨文件访问函数,声明函数
  extern void A(void);
  extern void B(void);
  //为了使用var1.c的全局非静态变量,声明它  
  extern int g_a;
  /*定义函数C*/
  void C(void)
  {
printf("%d\n", g_a); //可以访问
  }
  int main(void)
  {
A();
B();
A();
printf("%d\n", g_a);
return 0;
  }

  编译命令:gcc -o var var1.c var2.c //将var1.c和var2.c编译生成一个可执行文件var

c)切记:全局非静态变量特点

   1.此变量的使用范围:分两种情况

      1.如果是本文件访问,范围是从定义的地方开始依次向下所有的函数都可以访问,前面的函数无法访问

      2.如果是不同文件(跨文件)之间访问,范围是从声明的地方开始依次向下所有的函数都可以访问,前面的函数无法访问

   2.此变量分配的内存生命周期:从启动,运行程序时操作系统就会为其分配内存,直到程序结束,操作系统将其内存回收

   3.切记:全局非静态变量实际开发时少用,慎用,易出现乱序访问效果!如果非要用,一定要记得互斥访问保护,但是这种互斥保护又降低了代码的执行效率

2.10.详解全局静态变量:

a)形式:全局静态变量的定义和使用访问都是在同一个文件,并且此全局静态变量只能用于本文件,其他源文件不可访问

    vim var3.c

 void B(void)
    {
printf("g_a = %d\n", g_a); //不可以,gcc报没有定义的错误
    }
    static int  g_a = 10; //定义初始化全局静态变量,只能用于var3.c中   
   void A(void)
   {
printf("g_a = %d\n", g_a); //可以,函数内部访问全局非静态变量
   }  
   void C(void)
   {
printf("g_a = %d\n", g_a); //可以,函数内部访问全局非静态变量
   }

  vim var4.c 

 extern void A(void); //声明函数A
 extern void B(void); //声明函数B
 extern int g_a; //声明全局静态变量g_a
 int main(void)
  {
A();
B();
printf("g_a = %d\n", g_a); //gcc报错
return 0;
  }

  编译:gcc -o var var3.c var4.c

b)全局静态变量特点:

    1.此变量使用范围:只能用于定义变量的文件中(var3.c),并且是从定义的地方开始依次向下所有的其他函数都可以访问,之前的函数不可访问 

    2.此变量分配内存的生命周期:从启动,运行程序时操作系统就会为其分配内存直到程序结束,操作系统将其内存回收

    3.全局静态变量同样要少用,慎用,如果迫不得已非要访问,记得要互斥访问,但是它比全局非静态变量发生乱序现象的概率要小!

2.11.static关键字总结(笔试题必考):

        1.static修饰的全局变量只能用于本文件,其余文件不可访问

        2.static修饰的函数(定义函数时)只能用于本文件,其余文件不可调用访问

        3.static修饰的变量和函数将来用起来相对比较安全,起到了间接保护的作用,乱序发生的概率降低了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值