当前位置: 首页 > news >正文

C 语言学习笔记(函数)

内容提要

  • 函数
    • 函数的概述
    • 函数的分类
    • 函数的定义
    • 形参和实参
    • 函数的返回值

函数

函数的概述

  • **函数:**实现一定功能的,独立的代码模块,对于函数的使用,一定是先定义,后使用

  • 使用函数的优势:

    ①我们可以通过函数提供功能给别人使用。当然我们也可以是使用别人提供的函数,减少代码量。

    ②借助函数可以减少重复性代码。

    ③实现结构化(模块化:C语言中的模块其实就是多文件+函数)程序设计思想。

    关于结构化设计思想将大型的任务功能划分为相互独立的小型的任务模块来设计(多文件+函数)

  • 函数是C语言程序的基本组成单元:

    C语言程序必须包含一个main函数,可以包含零个或多个其他函数。

函数的分类

  • 按来源分:

    • **库函数:**C语言标准实现的并提供使用的函数,如:scanf()、printf()、fgets()、fputs()、strlen()…
    • **自定义函数:**需要程序员自行实现,开发中大部分函数都是自定义函数。
  • 按参数分:

    • **无参函数:**函数调用时,无需传递参数,可有可无返回值,如:show_all();
    • **有参函数:**函数调用时,需要参数传递数据,经常需要配套返回值来使用,如:printf(“%d\n”,12);
  • 按返回值分:

    • **有返回值函数:**函数执行后返回一个值,如:if(scanf(“%d”, &num) != 1)
    • **无返回值函数(void):**函数字仅执行操作,不返回值
  • 从函数调用的角度:

    • **主调函数:**主动去调用其他函数。(main函数只能作为主调函数)

    • **被调函数:**被其他函数调用的函数。

    • 举例:

      //主调函数
      int main()
      {//被调函数printf("hello world!\n");
      }
      

      很多时候,尤其是对于自定义函数,一个函数可能既是主调函数,又是被调函数。

      int fun_b()
      {printf("函数B\n");
      }
      // 主调函数
      int fun_a()
      {printf("函数A\n");// 被调函数fun_b();
      }int main()
      {//被调函数fun_a();
      }
      

      以上案例中,fun_a()对于fun_b()来说是主调函数,同时对于main()函数来说,它又是被调函数。

函数的定义

定义

语法:

[返回类型] 函数名([形参列表])     --函数头 | 函数首部
{函数体语句;                  --函数题: 整个{}包裹的内容都属于函数体,{}不能省略
}

函数头:

  • **返回类型:**函数返回值的类型
  • **函数名:**函数的名称,遵循标识符命名(不能以数字开头,只能包含字母、数字、下划线。建议:小写+下划线,举例: show_al1(),或者小驼峰命名法,第一个单词首字母小写,其他单词首字母大写,举例:showAll()
  • **形参列表:**用于接收主调函数传递的数据,如果有多个参数,使用","分隔,且每个形参都需要指明类型。

小贴士:

①形参列表(被调函数):主调函数给被调函数传递数据:主调函数→被调函数

②返回类型(被调函数):被调函数给主调函数返回数据:被调函数→主调函数

通过生活中的案例理解函数调用:

假设:饮料店的工作人员通过榨汁机榨取新鲜果汁

理解:

​ 工作人员:主调函数

​ 榨汁机:被调函数

​ 水果:传递的参数

​ 果汁:函数的返回值

​ 工作人员向榨汁机放入一个水果:主调函数调用被调函数,并传递数据

​ 工作人员用杯子接收榨汁机榨出的果汁:主调函数接受被调函数返回的数据

说明:

  • 函数的返回类型:就是返回值的类型,两个类型可以不同,但是必须能够进行转换,举例:

    double fun_a()   //函数的返回类型是 double
    {return 12//函数的返回值是12,其对应的类型是int(return后面跟的是返回值)
    }
    

    以上代码可以理解为:将int类型的12赋值给一个double类型的匿名变量(int→double)此时属于隐式转换 正确

    int fun_a()       //函数的返回类型是int
    {return 12.5;   //函数的返回值是int
    }
    

    以上代码可以理解为:将double类型的12.5赋值给一个int类型的匿名变量(double→int)舍弃小数点后的数据。

    案例:

    #include <stdio.h>double fun_a()
    {return 12;// 就是将int类型的12赋值给double类型的匿名变量 int --> double
    }
    int fun_b()
    {return 12.5;// 就是将double类型的12.5赋值给int类型的匿名变量 double --> int 此时会舍弃掉小数部分
    }
    double fun_c()
    {return 12.5;  // 就是将double类型的12.5赋值给double类型的匿名变量 double --> double
    }
    int main(int argc,char *argv[])
    {// 接收函数返回值,函数返回什么类型,就用什么类型接double result1 = fun_a();// 主调函数使用double来接收被调函数返回的double,double --> doubleprintf("%lf\n",result1);int result2 = fun_b();  // 主调函数使用int来接收被调函数返回的int,int --> intprintf("%d\n",result2);int result3 = (int)fun_c();  // 主调函数使用int来接收被调函数返回的double,int  --> (int)doubleprintf("%d\n",result3)return 0;
    }
    
  • 在C语言中无返回值时应明确使用void类型,举例:

    void test()  //此时这个函数,没有返回值,也就是它的返回只是retun;
    {ptintf("hello\n");
    }//下面写法等价于上面写法
    void test()
    {return;  //一般,这个return是省略不写的
    }
    
  • 在C语言中,C89标准允许函数的返回类型标识符可以省略,如果省略,默认返回int。C99/C11标准要求必须明确指定返回类型,不再支持默认int类型,举例:

    //写法1:main返回类型是int类型,默认的返回值是0,等价于写法2
    main()
    {....
    }//写法2:main的返回类型是int类型,返回值是0  推荐写法
    int main()
    {return 0;
    }
    
  • 函数中返回语句的形式为return(表达式)或者return 表达式

    //写法1
    int main()
    {return (0);
    }//写法2 推荐
    int main()
    {return 0;
    }
    
  • 如果参数列表中有多个形式参数,则它们之间要用,分隔;即使它们的类型相同,在形式参数中只能逐个将进行说明,举例:

    // 正确示例
    int avg(int x, int y, int z)
    {...
    }// 错误示例
    int avg(int x, y, z)  //不能省略每一个形参的类型
    {...
    }
    
  • 如果形参列表中没有参数,我们可以不写,也可以用void标识,举例:

    // 写法1  推荐
    int main()
    {...
    }// 写法2
    int main(void)
    {...
    }
    
案例
案例1
  • 需求:计算1~n之间自然数的阶乘值

  • 代码:

    #include <stido.h>/**
    *求1~n的阶乘值
    *建议:设计的函数,尽量让被调函数改动角小区,能够重复用,有主调函数去影响
    */
    size_t fun_1(int n)
    {int i;   //循环变量int s = 1//阶乘值,初始值是1for(i = 1; i <= n; i++) s*=i;return s; // 返回结果给主调函数
    }int main()
    {printf("1~12的阶乘结果是%d\n",fun_1(12));printf("1~20的阶乘结果是%d\n",fun_1(20));printf("1~30的阶乘结果是%d\n",fun_1(30));printf("1~40的阶乘结果是%d\n",fun_1(40));    return 0;
    }
    

    运行结果:

    在这里插入图片描述

    注意:这里计算结果为0,是因为数据太大,超过int存储范围,高位数据丢失,低位数据转出来为0,建议使用 unsigned long 类型。

案例2
  • 需求:计算一个圆台两个面的面积之和

    在这里插入图片描述

  • 代码:

    //定义PI
    #define PI 3.141599266
    /**
    *  定义一个函数,根据半径计算圆面积
    * @param r:半径
    * @return 面积
    */
    double cicle_area(double r)
    {// return PI * r * r;return PI * pow(r,2.0);  //pow(底数,指数):求r的2次方,我们有些库函数不是c标准函数,需要我们做特殊处理,比如pow需要在编译时加 -lm
    }int main()
    {//定义两个半径,两个面积double r1,r2,area1,area2;printf("请输入两个圆的半径:\n");scanf("%lf,%lf",&r1,&r2);//调用函数计算圆的面积area1 = cicle_area(r1);area2 = cicle_area(r2);printf("一个圆台两个面面积之和是%.2lf\n",area1 + area2);return 0;
    }
    
  • 编译命令:

    gcc demo03.c -lm   //pow() 不是C语言标准函数,无法自动链接,血药手动链接  -lm linked math  链接数学库
    

形参和实参

形参(形式参数)

定义

函数定义时指定的参数,形参是用来接收数据的,函数定义时,系统不会为形参申请内存,只有当函数调用时,系统才会为形参申请内存。主要用于存储实际参数,并且当函数返回时(执行return),系统会自动回收为形参申请的内存资源。

  • C语言中所有参数传递都是值传递
  • 若要修改实参,需传递指针

案例

  • 需求:判断一个数是偶数还是奇数

  • 代码:

    /**
    * 方式1
    */
    void fun0(int n) // 这里的n就是形式参数
    {if(n %2 == 0)printf("%d是偶数!\n",n);else printf("%d是奇数!n",n);
    }/**
    * 方式2
    */
    void fun1(int n) // 这里的n就是形式参数
    {if(n %2 == 0){   printf("%d是偶数!\n",n);return; //提前结束函数,return后的代码不再执行}printf("%d是奇数!n",n);
    }/**
    * 方式3
    */
    void fun2(int n) // 这里的n就是形式参数
    {if(n %2 == 0){   printf("%d是偶数!\n",n);return -1; }printf("%d是奇数!n",n);return 0}int main()
    {fune(5);fun1(5);fun2(5);return 0;
    }
    
实参(实际参数)
定义

实参是函数调用时由主调函数传递给被调函数的具体的数据。实参可以是常量、变量或者表达式

关键特性

1.类型多样性:

  • 实参可以是常量、变量或者表达式。

  • 例如:

    fun(12);       //常量作为实参
    fun(a);        //变量作为实参
    fun(a + 12);   //表达式作为实参
    

2.类型转换

  • 当实参和形参类型不同时,会按照赋值规则进行隐式类型转换

  • 类型转换可能导致精度损失。

  • 例如:

    /**
    * 求一个数的最大值
    */
    double fabs(double a)
    {return a < 0 ? -a : a;
    }int main()
    {int x = 12, y = -12;//函数调用时,自动将int类型隐式转换为double类型;//函数转换时,需要将duble类型转为int类型,此时需要显示转换int x1 = (int)fabs(x); int y1 = (int)fabs(y); //同上
    }
    

    注意:函数调用的时候,通过实参给形参赋值。

3.单向值传递

  • C语言采用单向值传递机制(赋值的方向:实参→形参)

  • 实参仅将其值赋给形参,不传递参本身

  • 形参值的改变不会影响实参。

  • 案例:

    int modify(int n)//n的变量地址:0x11
    {n=20; // 修改形参n = 20
    }int main()
    {int n = 10;//实参变量 n=10    n的变量地址:0x22modify(n);printf("%d\n",n); // 10
    }
    

    在这里插入图片描述

4.内存独立性

  • 实参和形参在内存中占据不同的空间

  • 形参拥有独立的内存地址

  • 演示:

    #include <stdio.h>
    int fun(int n)  // n 是形参
    {printf("形参n的值: %d\n", n);n += 5;  // 修改形参return n;
    }int main()
    {int a = 10;printf("调用前实参a的值: %d\n", a);// 变量作为实参int result = fun(a);  // a 是实参printf("调用后实参a的值: %d\n", a);  // a保持不变printf("函数返回值: %d\n", result);// 常量作为实参fun(12);  // 字面量12是实参// 表达式作为实参fun(a + 12);  // 表达式a+12是实参return 0;
    }
    

    上述示例程序输出:

    在这里插入图片描述

案例

  • 需求:输入4个整数,要求一个函数求出最大数

  • 分析:

    • 设计一个函数,这个函数只是西安2个数求最大值
    • 多次复用这个函数实现最终求值
  • 代码:

    /**
    * 定义一个函数,求两个数中的最大值
    *@param x,y
    *@return 最大值
    */
    int max(int x, int y)
    {return x > y ? x : y;
    }int main()
    {//定义4个变量,用来接收控制台输入int a, b, c, d, m;printf("请输入四个整数:\n");scanf("%d%d%d%d",&a,&b,&c,&d);// 求a,b中的最大值m = max(a,b);// 求a,b,c中的最大值m = max(m,c);// 求a,b,c,d中的最大值m = max(m,d);printf("%d,%d,%d,%d中的最大数是%d", a, b, c, d, m);return 0:
    }
    
  • 运行结果:

    在这里插入图片描述

函数的返回值

定义
  • 若不需要返回值,函数可以没有return语句。

    //如果返回类型是void,return 关键字可以省略
    void fun1()
    {...
    }
    // 这种写法,return关键字也可以省略,但是此时默认返回是 return 0
    int fun2()
    {}// 这种写法,return关键字也可以省略,但是此时默认返回是 return 0
    fun3() // 如果不写返回类型,默认返回int
    {} 
    
  • 一个函数中可以有多个return语句,但是任一时刻只有一个return语句被执行。

    int eq(int num)
    {if(num % 2 ==0){printf("%d是偶数!\n", num);return 0;}printf("%d是奇数!\n", num);return 0;
    }
    
  • 返回值类型一般情况下要和函数中return语句的数据类型一致,如果不一致,以函数定义指定的返回值类型为标准。

    在这里插入图片描述

案例
  • 需求:输入两个整数,要求用一个函数求出最大数

  • 案例1:不涉及类型转换

    #include <stdio.h>
    /**
    * 定义一个函数,求两个数中的最大数
    * @param x,y  外部传入的整数
    * @return 最大数
    */ int get_max(int x, int y)
    {if (x > y) return x;return y;
    }
    int main(int argc,char *argv[])
    {// 定义三个变量,用来存储控制台输入的两个整数以及最大值int a,b,max;// 通过控制台录入数据printf("请输入两个整数:\n");scanf("%d%d",&a,&b);// 调用函数进行比较max = get_max(a,b);printf("%d,%d中的最大值是%d\n",a,b,max);return 0;
    }
    
  • 案例2:设计类型转换

    #include <stdio.h>
    /**
    * 定义一个函数,求两个数中的最大数
    * @param x,y  外部传入的整数
    * @return 最大数
    */ 
    double get_max(int x, int y)
    {if (x > y) return x;return y;
    }
    int main(int argc,char *argv[])
    {// 定义三个变量,用来存储控制台输入的两个整数以及最大值int a,b,max;// 通过控制台录入数据printf("请输入两个整数:\n");scanf("%d%d",&a,&b);// 调用函数进行比较max = (int)get_max(a,b); // double → int    需要显示转换printf("%d,%d中的最大值是%d\n",a,b,max);return 0;
    }
    
  • 案例3:涉及类型转换-显示转换

    #include <stdio.h>
    /**
    * 定义一个函数,求两个数中的最大数
    * @param x,y  外部传入的整数
    * @return 最大数
    */ int get_max(int x, int y)
    {double z;z = x > y ? x : y;return (int)z;  // double → int
    }
    int main(int argc,char *argv[])
    {// 定义三个变量,用来存储控制台输入的两个整数以及最大值int a,b,max;// 通过控制台录入数据printf("请输入两个整数:\n");scanf("%d%d",&a,&b);// 调用函数进行比较max = get_max(a,b);printf("%d,%d中的最大值是%d\n",a,b,max);return 0;
    }
    

相关文章:

  • 【Linux】第十九章 管理SELinux安全性
  • 【Linux驱动】Linux 按键驱动开发指南
  • 【回溯 剪支 状态压缩】# P10419 [蓝桥杯 2023 国 A] 01 游戏|普及+
  • 【第三篇】 SpringBoot项目中的属性配置
  • 动态规划(4)可视化理解:图形化思考
  • SparkSQL基本操作
  • ​在 ASP.NET 中,HTTP 处理程序(HttpHandler)是处理 HTTP 请求的核心组件​
  • 嵌入式通信协议(二)——IIC总线
  • Flink Table SQL
  • Flink并行度与分区机制深度解析
  • 【c库主要功能】
  • 深入理解Java中的Minor GC、Major GC和Full GC
  • OpenHarmony 5.0状态栏息屏状态下充电然后亮屏会出现电量跳变情况
  • AI工程 新技术追踪 探讨
  • 23种设计模式考试趋势分析之——适配器(Adapter)设计模式——求三连
  • Android 自定义悬浮拖动吸附按钮
  • 【赵渝强老师】Memcached的路由算法
  • Serverless技术深度整合:从冷启动优化到边缘场景落地
  • 锂电池SOC估计EKF仿真模型
  • 人工智能赋能产业升级:AI在智能制造、智慧城市等领域的应用实践
  • 《习近平新时代中国特色社会主义思想学习论丛》第十一辑至第十五辑出版发行
  • 慢品巴陵,看总编辑眼中的岳阳如何书写“山水人文答卷”
  • 外媒:哈马斯一名高级指挥官尸体被发现,系辛瓦尔弟弟
  • 《掩耳盗邻》:富人劫富,是犯罪,也是赎罪?
  • 国家统计局公布2024年城镇单位就业人员年平均工资情况
  • 大陆非遗项目打铁花、英歌舞将在台演出