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

C语言:函数基础

Day 11-C语言

函数

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

  • 使用函数的优势:

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

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

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

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

  • 函数的作用:
    (1) 代码复用:避免重复编写相同功能的代码。
    (2) 模块化设计:将复杂程序拆分成多个小功能模块,每个函数负责一个独立任务,使代码逻辑结构更加清晰。
    (3) 便于维护和调试:单个函数功能单一,出现问题容易定位和修改,不需要改动整个程序。
    (4) 提高开发效率:便于多人协同开发时,分工明确,编写不同函数,最终组合成完整程序

  • 函数是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");
      }// fun_a是主调函数
      int fun_a()
      {printf("函数A\n");// fun_b是背调函数fun_b();
      }int main()
      {// fun_a是被调函数fun_a();
      }
      

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

函数的定义

定义

语法:

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

函数头:

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

小贴士:

①形参:主调函数给被调函数传递数据 主调函数 → 被调函数

②返回值:被调函数给主调函数返回数据 被调函数 → 主调函数

说明:

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

    double fun_a()  //函数返回类型 :double
    {return 12;  //函数的返回值是int
    }
    //分析:此时需要转换int[] fun_b()  //函数返回类型int[]
    {return 12;  //函数的返回值是int
    }
    //int不能转换为int[],错误!!int fun_c()   //函数返回类型 :int
    {return 12.5;  //函数的返回值是double
    }
    // double可转化为int类型
    

    理解:

    可以将函数的返回类型理解为 变量的类型,将函数的返回值理解为 变量的值

  • 在C语言中无返回值时应明确使用viod类型(空类型/无类型)举例:

    void test() // 无返回值,提前结束写:return
    {printf("hello world!\n");
    }// 等价于上面写法
    void test()
    {printf("hello world!\n");return;  // 一般省略不写
    }
    
  • 在C语言中,C89标准允许函数的返回类型标识符可以省略,如果省略,默认返回int。C99/C11标准要求明确指定返回类型,不支持int类型。举例

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

    int main()
    {return (0);
    }
    //等价于
    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)
    {...
    }
    
  • C89开始提供了变长参数,需要引入<stdarg.h>

    语法:

    [返回类型] 函数名(参数1,...)
    {...
    }
    
    • 举例:

      #include <stdio.h>
      #include <stdarg.h>// 计算n个整数的平均值
      double average(int n, ...) { // ...只能放在 具体的参数列表的后面va_list args;       // 声明参数列表对象int sum = 0;va_start(args, n);  // 初始化参数列表,n是最后一个固定参数// 遍历所有可变参数for (int i = 0; i < n; i++) {// 获取一个int类型的参数sum += va_arg(args, int);}va_end(args);       // 清理参数列表return (double)sum / n;
      }int main() {printf("平均值: %.2f\n", average(3, 10, 20, 30));   // 20.00printf("平均值: %.2f\n", average(5, 1, 2, 3, 4, 5)); // 3.00return 0;
      }
      
    案例
案例1:
  • 需求:计算1~n之间的阶乘

  • 代码

    #include <stdio.h>/**
    *定义一个函数,实现阶乘
    *@param n:阶乘上限
    *@return n n的阶乘值
    */size_t test_1(int n)
    {int i; // 循环变量size_t s = 1; // 初始值1for (i = 1; i <= n;i++) s *= i;return s;}int main(int argc,char *argv[])
    {printf("1~3的阶乘结果是:%lu\n",test_1(3));printf("1~5的阶乘结果是:%lu\n",test_1(5));printf("1~12的阶乘结果是:%lu\n",test_1(12));printf("1~20的阶乘结果是:%lu\n",test_1(20));return 0;
    }
    
  • 运行结果

在这里插入图片描述

注意:这里计算结果为0,由于数据太大,超过存储范围,建议使用unsigned long类型

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

  • 实现

    1. 定义一个函数,传递一个圆的半径,返回一个圆的面积
  • 代码

    #include <stdio.h>
    #include <math.h>#define PI 3.1415926/*** 定义一个函数,计算圆的面积* @param r:圆的半径* @return 圆的面积*/double cicle_area(double r)
    {return PI * pow(r,2.0);// return PI * r * r;
    }int main(int argc,char *argv[])
    {double r1,r2,area1,area2;printf("请输入两个圆的半径:\n");scanf("%lf%lf",&r1,&r2);//计算面积area1 = cicle_area(r1);area2 = cicle_area(r2);printf("两个圆的面积之和为%lf\n",area1+area2);return 0;
    }
    

注意:

编译代码命令 gcc demo01.c -lm

  • 运行结果:
    在这里插入图片描述

形参和实参

形式参数

定义:

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

  • C语言中所有的参数传递都是值传递。
  • 若要修改实参,需要传递指针,指针本质上也是值传递
案例
  • 需求:判断一个数是偶数还是奇数

  • 代码:

    #include <stdio.h>/*** 方式一*/
    void fun1(int n) // n为形参
    {if (n % 2 == 0){printf("%d是偶数!\n",n);return;}printf("%d是奇数!\n",n);
    }/*** 方式二*/
    int fun2(int n) // n为形参
    {if (n % 2 == 0){printf("%d是偶数!\n",n);return -1;}printf("%d是奇数!\n",n);return 0;
    }int main(int argc,char *argv[])
    {fun1(4);fun2(5);return 0;
    }
    
  • 运行结果:

    在这里插入图片描述

实际参数
定义

实参是函数调用时由主调函数传递给被调函数的具体数据。实参可以是常量、变量、表达式、带有返回值的函数等

关键特性

1.类型多样性

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

  • 例如

    fun(12);      // 常量作为实参
    fun(a);       // 变量作为实参
    fun(a + 12);  // 表达式作为实参
    fun(func());  // 带有返回值的函数作为实参
    

2.类型转换

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

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

  • 例如:

    #include <stdio.h>/*** 求一个数的绝对值*/
    double fabs(double a)
    {return a < 0 ? -a : a;
    }int main()
    {int x = 12, y = -12;int x1 = (int)fabs(x);  // x会被隐式转换为double,fabs返回的是double类型数据int y1 = (int)fabs(y);
    }
    

    注意:函数调用时,通过实参给形参赋值。形参类似于变量,实参类似于变量的值

    函数调用时:

    主调函数通过实参给被调函数的形参赋值,可理解为:将主调函数据的值赋值给被调函数的变量。

    函数返回时:

    被调函数通过返回值给主调函数赋值,可理解为:将被调函数的值赋值给主调函数的变量。

3.单项值传递

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

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

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

  • 案例:

    int modify(int n) // n的变量地址:0x11
    {n = 20;  // 修改 0x11这个空间的数据位20  n = 20return n;
    }int main()
    {int n = 10;   // n的变量地址:0x21modify(n);    // 将0x21中的数据赋值给0x11这个空间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); // 10// 变量作为实参int res = fun(a); // a 是实参printf("调用前实参a的值:%d\n", a); // 10printf("函数返回值:%d\n", res);   // 15// 常量作为实参fun(12);   //字面量12作为实参// 表达式作为实参fun(a + 12); // 表达式作为实参return 0;
    }/*调用前实参a的值: 10形参n的值: 10调用后实参a的值: 10函数返回值: 15形参n的值: 12形参n的值: 22 */
    
案例:
  • 需求:输入4个整数,用函数求出最大值

  • 分析

    • 设计一个函数,只求2个数中的最大值
    • 多次复用
  • 代码

    #include <stdio.h>/*** 定义一个函数,求2个数中的最大* @param x,y:参与比较的整数* @return 返回最大值*/
    int get_max(int x,int y)
    {return x > y ? x : y;
    }int main(int argc,char *argv[])
    {//创建变量4个int a,b,c,d;//创建变量,存储最大值int max;printf("请输入4个整数:\n");scanf("%d%d%d%d",&a,&b,&c,&d);max = get_max(a,b);max = get_max(max,c);max = get_max(max,d);printf("%d %d %d %d中最大的是%d\n",a,b,c,d,max);return 0;
    }
    
  • 运行结果

在这里插入图片描述

函数的返回值

定义
  • 若不需要返回值,函数可以没有return语句
  • 一个函数中可以有多个return语句,但是同一时刻只有一个return被执行。
  • 返回类型一般情况下要和函数中return语句返回的数据类型一致,如果不一致,要符合C语言中的隐式转换规则。
案例
  • 需求:输入两个整数,用一个函数求最大值

  • 实现1:不涉及类型转换

    #include <stdio.h>int get_max(int x, int y)
    {if (x > y) return x;return y;
    }int main()
    {int a,b,max;printf("请输入两个整数:\n");scanf("%d%d",&a,&b);max = get_max(a,b);printf("%d,%d中的最大值是%d\n",a,b,max);
    }
    
  • 实现2:设计类型转换-隐式转换

    #include <stdio.h>double get_max(int x, int y)  // int隐身转换为double
    {if (x > y) return x;return y;
    }int main()
    {int a,b,max;printf("请输入两个整数:\n");scanf("%d%d",&a,&b);max = (int)get_max(a,b);  // 显示转换printf("%d,%d中的最大值是%d\n",a,b,max);
    }
    
  • 实现3:涉及类型转换-显示转换

    #include <stdio.h>int get_max(int x, int y)  // 将double类型转换为int类型,可以隐式转换,也可以显示转换
    {double z;z = x > y ? x : y;return (int)z; // 显示转换
    }int main()
    {int a,b,max;printf("请输入两个整数:\n");scanf("%d%d",&a,&b);max = get_max(a,b);printf("%d,%d中的最大值是%d\n",a,b,max);
    }
    

函数的调用

调用方式

①函数语句:

test();  // 对于无返回值的函数
int res = max(2,4); 

②函数表达式

4 + max(2,4)
scanf("%d",&num != 1)
(c = getchar() != '\0')

③函数参数

printf("%d",(int)fabs(number));  //函数做实参

注意:函数可以作为函数的实参,如果要作为形参,必须使用函数指针

在一个函数中调用另一个函数具备以下条件:

  • 被调用的函数必须是已经定义的函数。
  • 若使用库函数,应在本文件开头用#include包含其对应的头文件。
  • 若使用自定义函数,自定义函数又在主调函数的后面,则应在主调函数中对被调函数进行声明。声明的作用是把函数名、函数参数的个数和类型等信息通知编译系统,以便于在遇到函数时,编译系统能正确识别函数,并检查函数调用的合法性。

函数的声明

函数调用时,往往要

定义

完整的函数使用分为三部分:

  • 函数声明

    int max(int x,int y,double z);  // 函数只保留
    int max(int,int,double);  //函数声明时,可省略形参名
    

    函数声明如果是在同一个文件,一定要定义在文件中所有函数定义的最前面。如果有对应的.h文件,可以将函数的声明抽取到.h中。

  • 函数定义

    int max(int x, int y, double z)     // 函数定义时,一定不能省略形参名称
    {return x > y ? x : y > z ? y : (int)z;
    }
    

    函数定义的时候,不能省略形参的数据类型、参数个数、参数名称,位置要和函数声明完全一致。

    注意:函数定义时参数列表要与函数声明时的参数列表完全对应,同时函数定义要保留形参名称

  • 函数调用

    int main()
    {printf("%d\n", max(4,5,6));
    }
    
作用

C语言的函数声明时为了提前告诉编译系统函数的名称、返回类型和参数,这样在函数实际定义之前就能安全调用它,避免编译错误,同时检查参数和返回值是否正确。相当于给编译器一个“预告”,确保代码正确编译和运行。

使用
  • 错误示例:被调函数写在主调函数之后

    // 主调函数
    int main()
    {printf("%d\n", add(12,13));// 编译报错,因为函数未经过声明,编译系统无法检查函数的合法性
    }// 被调函数
    int add(int x, int y)
    {return x + y;
    }
    
  • 正确示例:主调函数写在被调函数之后

    // 被调函数
    int add(int x, int y)
    {return x + y;
    }// 主调函数
    int main()
    {printf("%d\n", add(12,13));
    }
    

    注意:如果函数的调用比较简单,如a函数调用b函数,b函数定义在a函数之前,此时是可以省略函数声明的。

  • 正确演示:被调函数和主调函数无法区分前后,必须要增加函数声明

    // 函数声明
    void funa(int, int);
    void funb(int, int);// 函数定义
    void funb(int a, int b)
    {...// 函数调用funa();
    }void funa(int a, int b)
    {...// 函数调用funb();
    }int main()
    {// 函数调用funa(12,13);
    }
    

    声明的方式

    • 函数头加上分号

        int add(int a, int b);
      
    • 函数头加上分号,可省略形参名称,但不能省略参数类型

        int add(int, int);
      
http://www.dtcms.com/a/292502.html

相关文章:

  • day059-zabbix自定义监控与自动发现
  • Node.js:Web模块、Express框架
  • es6中的symbol基础知识
  • 在Android开发中,如何获取到手机设备的PIN码?
  • 如何安装CMake较新的版本
  • Apache Ignite 长事务终止机制
  • 精密全波整流电路(一)
  • torchvision.transforms 与 MONAI 数据增强的异同
  • Cloud 与 VPS 的区别:如何选择最适合你的服务器解决方案?
  • stream流入门
  • 【打怪升级 - 01】保姆级机器视觉入门指南:硬件选型 + CUDA/cuDNN/Miniconda/PyTorch 安装全流程(附版本匹配秘籍)
  • vmware 克隆虚拟机,报错:克隆时出错:指定不存在的设备。然后电脑卡死,只能强制关机再开机。
  • FastDFS 6.11.0 单机环境搭建与测试(附 Nginx 集成)+ docker构建+k8s启动文件
  • 用org.apache.pdfbox 转换 PDF 到 图片格式
  • KafkaMQ 日志采集最佳实践
  • Python 正则表达式:入门到实战
  • 日常随笔-React摘要
  • 【ROS/DDS】FastDDS :编写FastDDS程序实现与ROS2 通讯(四)
  • 深入浅出理解 TCP 与 UDP:网络传输协议的核心差异与应用
  • 平台端用户管理功能设计全解:从分类管控到审核闭环
  • 基于springboot的疫苗发布和接种预约系统(论文+开题报告)
  • 实现分布式锁
  • 腾讯云SDK
  • 论文笔记:Parameter Competition Balancing for Model Merging
  • MongoDB频繁掉线频繁断开服务的核心原因以及解决方案-卓伊凡|贝贝|莉莉|糖果
  • 在Windows 10/11上使用Qt和SOEM构建EtherCAT主站:工业控制新纪元
  • 【Axure视频教程】形状地图
  • Qt 事件处理机制深入剖析
  • 【OpenCV篇】OpenCV——01day.图像基础
  • 通俗易懂循环神经网络(RNN)指南