C语言参悟-函数

news2025/2/28 23:46:58

C语言参悟-函数

  • 一、概述
  • 二、函数
    • 1. 函数的构成
    • 2. 函数参数
    • 3. 函数名
    • 4. 函数返回值
    • 5. 函数的工作
      • 1. 程序栈
      • 2、栈帧的组织
  • 三、函数递归
  • 四、函数指针

一、概述

首先,什么是函数?函数(function)是完成特定任务的独立程序代码单元。在现实生活中,为了解决某一个问题,我们需要把解决这个问题的大任务分解成单个单个的小任务。因为可能在这个任务里面有很多事重复类似的任务,对这种重复的任务我们只需要共用一个方法去解决即可。

这样的方法同样适应计算机里的任务,在C语言里面就提供了这样的方式,就是用函数来代替处理重复任务的方法。

函数的目的也就解决一个小任务,可以说函数算是C语言处理的最小单位了。

用一个一个的函数组合起来就能解决这个大的任务。可以说函数就是积木。我们为了搭建出下面这个房子,用了不同形状的小积木块,这些小积木块 如:在这里插入图片描述在这里插入图片描述 构成这么大的积木。

当然我们还可以利用小的积木,拼成的二级小积木当成一个模块使用,就比如 在这里插入图片描述

在这里插入图片描述

为什么要使用函数?首先,使用函数可以省去编写重复代码的苦差。如果程序要多次完成某项任务(复用性),那么只需编写一个合适的函数,就可以在需要时使用这个函数,或者在不同的程序中使用该函数,就像许多程序中使用putchar()一样。其次,即使程序只完成某项任务一次,也值得使用函数。因为函数让程序更加模块化,从而提高了程序代码的可读性(扩展性),更方便后期修改、完善。

二、函数

1. 函数的构成

像下面的代码就是较为恰当的定义和使用函数,就像 show_n_char() 函数是放在主函数之前的一个声明,也叫函数原型,这个能告诉编译器怎么去使用这个函数,函数原型提供了函数外部的所有接口信息,函数的实现其实就可以放在其他地方就行了。

对于函数原型可以有两种形式

就行下面的代码里面说的

// 函数原型方式1
void show_n_char(char ch, int num);

// 函数原型方式2
// void show_n_char(char, int);

//主函数
int main()
{
	show_n_char('d', 5);
	return 0;
}

//函数定义
void show_n_char(char ch, int num)
{
	printf("ch: %c, num: %d", ch, num);
}

一个完整的函数总是由 这几部分构成:

返回值 函数名(函数参数)
{
函数体
}

以下有几个指的注意的事项:

  1. 返回值、函数参数都可以设置为空 void,或者返回有效值
  2. 函数名不可缺省
  3. 函数体也可以为空

一个函数可以理解为一个小的模块,一个黑盒子,函数也许需要外部输入、也可能需要输出东西,或者不输出不输入调用即可更改某些属性。这种表现形式完全是由开发人员决定的。

比如需要输入输出的一个函数

int MultipleNum(int num1, int num2)
{
	return (num1+num2)*(num1+num2);
}

只有输出的函数

int MultipleNum()
{
	return (12+12)*(14+14);
}

或者无输入输入

void MultipleNum()
{
	printf(num: %d", 12+12)*(14+14));
}

2. 函数参数

int MultipleNum(int num1, int num2)
{
	return (num1+num2)*(num1+num2);
}

以上面的例子说,函数参数其实就是指的 int MultipleNum (int num1, int num2) 里面的 (int num1, int num2)

函数参数是不定的,括号里面可以没有东西的话可以写成 像这种 ()、(void)

多个参数就需要用 ‘,’ 逗号分隔,每个参数都是有类型名和变量名,这个变量名也叫形参,这个变量是后面能参与到函数体里面去使用的。当然了,也不是说形参变量定义了就必须被使用哈,这些都是不确定的啦。

3. 函数名

这个没啥特殊的,命名反正要体现出你需要的那种格式,而且命名不能过长。

4. 函数返回值

函数的返回值也是可有可无的,这个也是由使用者确定的。

在不需要返回值的时候就要写 void ,同时在函数体中可有不写return 语句,或者写 return;

像下面两种的写法都是对的。

void MultipleNum(int num1, int num2)
{
	//....
	return ;
}

void MultipleNum(int num1, int num2)
{
	//....
}

5. 函数的工作

1. 程序栈

程序栈是支持函数执行的内存区域,通常和堆共享。也就是说,它们共享同一块内存区域。程序栈通常占据这块区域的下部,而堆用的则是上部。

下图就简单说明程序栈堆的模型
在这里插入图片描述
调用函数时,函数的栈帧被推到栈上,栈向上“长出”一个栈帧。当函数终止时,其栈帧从程序栈上弹出。可以理解为直到main函数从程序栈里弹出后,程序就结束了。

  • 栈帧所使用的内存不会被清理,但最终可能会被推到程序栈上的另一个栈帧覆盖

有时候我们听到的一个术语:栈溢出

可以理解为程序栈一直压栈,导致程序的的内存需要超过了真实物理内存,然后程序奔溃;我了解的情况就是有几个:

  • 1、递归函数的递归条件错误,导致一直递归,一直无限调用递归函数
  • 2、存在调用函数的死循环

示意图:

在这里插入图片描述

2、栈帧的组织

栈帧由以下几种元素组成。

  • 返回地址
    函数完成后要返回的程序内部地址。

  • 局部数据存储
    为局部变量分配的内存。

  • 参数存储
    为函数参数分配的内存。

  • 栈指针和基指针
    运行时系统用来管理栈的指针。(我们写代码不用管,系统帮我们来完成)栈指针通常指向栈顶部。基指针(帧指针)通常存在并指向栈帧内部的地址,比如返回地址,用来协助访问栈帧内部的元素。这两个指针都不是C指针,它们是运行时系统管理程序栈的地址

注意要点:

  • 系统在创建栈帧时,将参数以跟声明时相反的顺序推到帧上,然后是返回地址,最后推入局部变量,
float average(int*arr,int size){
	int sum = 0;
	printf("arr:%p\n",&arr);
	printf("size:%p\n",&size);
	printf("sum:%p\n",&sum);
	for(int i=0;i<size;i++)
	{
		sum+=arr[i];
	}
	return(sum*1.0f)/size;
}

这个代码中,在函数栈里分配空间顺序是: size–>arr–>返回计算值地址–>sum.

在实际程序里面的层层调用也是按照下面这些形式,比如在 main函数中引入了 a(),a() 引用了 b()。

实际的执行情况就按照下面的 1 2 3 4 5 6 7 8 9 顺序运行的。
在这里插入图片描述

三、函数递归

C允许函数调用它自己,这种调用过程称为递归(recursion)。

递归有时难以捉摸,有时却很方便实用。

结束递归是使用递归的难点,因为如果递归代码中没有终止递归的条件测试部分,一个调用自己的函数会无限递归。无限递归的归属就是程序栈溢出崩溃。

可以使用循环的地方通常都可以使用递归。有时用循环解决问题比较好,但有时用递归更好。递归方案更简洁,但效率却没有循环高。

四、函数指针

因为函数指针和指针部分是有重叠的,函数指针与内存的一些分布有关系,感兴趣可以参考一下 我之前的笔记

指针与函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1104575.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

五金仓库管理员的工作内容

近几年来&#xff0c;很多企业会发现&#xff0c;经营增长的压力越来越大&#xff0c;主要原因包括外部局势的巨变&#xff0c;市场捉摸不定&#xff1b;企业之间竞争越来越剧烈&#xff0c;企业的利润空间不断被压缩&#xff1b;员工越来越个性化、多元化&#xff0c;管理困难…

文档外发控制与安全:实现高效协作与数据安全的关键

随着企业数据量的不断增加&#xff0c;文档外发成为了一个不可避免的需求。然而&#xff0c;很多企业在文档外发过程中存在着很多问题&#xff0c;如数据泄露、信息误用等。因此&#xff0c;如何保证文档外发的安全性和高效性成为了企业亟待解决的问题。飞驰云联Ftrans的文件收…

软件工程与计算(十四)详细设计中面向对象方法下的模块化

一.面向对象中的模块 1.类 模块化是消除软件复杂度的一个重要方法&#xff0c;每个代码片段相互独立&#xff0c;这样能够提高可维护性。在面向对象方法中&#xff0c;代码片段最重要的类&#xff0c;整个类的所有代码联合起来构成独立的代码片段。 模块化希望代码片段由两部…

以烟草行业为例,聊聊如何基于 PLC + OPC + TDengine,快速搭建工业生产监测系统

在烟草工业场景里&#xff0c;多数设备的自动控制都是通过 PLC 可编程逻辑控制器来实现的&#xff0c;PLC 再将采集的数据汇聚至 OPC 服务器。传统的 PI System、实时数据库、组态软件等与 OPC 相连&#xff0c;提供分析、可视化、报警等功能&#xff0c;这类系统存在一些问题&…

Tortoise SVN 察看本地缓存密码

1、打开设置&#xff08;Settings&#xff09; 2、查看保存的数据 3、打开鉴权数据 4、查看密码 CTRLSHIFT双击表格&#xff0c;就会出现一列密码列 &#xff08;我的是Mac PD虚拟Win11&#xff0c;CTRLSHIFTOPTION双击表格&#xff09; 原文见这里&#xff1a; Recover SVN …

解密新一代商业智能软件百度GBI,看看它有多牛!

10月17日&#xff0c;2023百度世界大会开幕了&#xff0c;大家都关注了吗&#xff1f;本次大会上发布了关于商业智能&#xff08;Business Intelligence&#xff09;的工具百度GBI。那么首先给大家列出2022年全球商业智能(BI)和分析平台市场的相关数据&#xff1a; 总规模&…

HTML 常用标签及练习

常用标签 <head>中的标签 概述 head中的内容不显示到页面上 标签说明<title>定义网页的标题<meta>定义网页的基本信息&#xff08;供搜索引擎&#xff09;<style>定义CSS样式<link>链接外部CSS文件或脚本文件<script>定义脚本语言<…

Docker逃逸---CVE-2019-5736浅析

一、产生原因 Docker执行命令时&#xff0c;先向镜像管理的containerd发送gRPC请求&#xff0c;containerd收到请求后&#xff0c;再发送给具体的容器管理containerd-shim&#xff0c;shim根据OCI协议将命令发送给runc执行&#xff0c;所以实际上执行命令的是runc 漏洞大概意思…

C++ Eigen 矩阵运算

文章目录 1 Eigen的安装与CMakeLists.txt的编写1.1、Eigen的安装1.2、Eigen的CMakeLists.txt编写1.3、版本查看 2、Eigen的头文件3、Eigen的基础3.1 Eigen初始化3.1.1 一些常用的初始化方法 3.2 矩阵大小3.3 Eigen矩阵类3.4 Eigen矩阵的创建4 Eigen的Array类4.1 Array的初始化4…

JavaScript爬虫程序实现自动化爬取tiktok数据教程

以下是一个使用 request-promise 和 JavaScript 的爬虫程序&#xff0c;用于爬取tiktok的内容。此程序使用了 https://www.duoip.cn/get_proxy 这段代码。 // 引入 request-promise 库 const rp require(request-promise);// 定义 get\_proxy 函数 function get_proxy() {retu…

千兆光模块存在哪些局限性

千兆光模块是目前使用最广泛的光模块之一&#xff0c;可以实现1Gbps的传输速度。随着科技的进步和应用场景的多样性&#xff0c;千兆光模块也因其固有的局限性而面临越来越多的挑战。以下是千兆光模块的局限性和如何克服这些局限性的讨论&#xff1a; 千兆光模块可以实现最大…

钻井平台升降装置锁紧液压系统比例阀放大器

钻井平台升降装置锁紧液压系统为钻井平台桩腿的锁紧装置提供动力&#xff0c;通过液压马达驱动垂直布置的上下螺杆传动装置的伸缩&#xff0c;使锁紧齿条和桩腿上的弦管齿条啮合和分离&#xff0c;实现平台的锁紧、负载转移、均载和解锁。

05 依赖倒置原则

官方定义&#xff1a; 依赖倒置原则&#xff08;Dependence Inversion Principle&#xff0c;DIP&#xff09;是指在设计代码架构 时&#xff0c;高层模块不应该依赖于底层模块&#xff0c;二者都应该依赖于抽象。抽象不应该依 赖于细节&#xff0c;细节应该依赖于抽象。 通俗…

Linux系统添加硬件设备流程

Linux系统添加硬件设备的流程 添加硬盘设备的操作&#xff1a; 一、在虚拟机中模拟添加入一块新的硬盘存储设备 &#xff08;步骤见《Linux这么学》P191&#xff09; 二、依次进行分区、格式化、挂载操作 1.分区 &#xff08;1&#xff09;新建、修改及删除磁盘的分区表信息 #…

PAM从入门到精通(五)

接前一篇文章&#xff1a;PAM从入门到精通&#xff08;四&#xff09; 本文参考&#xff1a; 《The Linux-PAM Application Developers Guide》 先再来重温一下PAM系统架构&#xff1a; 更加形象的形式&#xff1a; 五、主要函数详解 3. pam_set_item 概述&#xff1a; 设置…

可追溯性在MES管理系统解决方案中的重要性

在当今制造业中&#xff0c;“可追溯性”已经变得至关重要。它能够在产品出现问题时迅速定位问题源头&#xff0c;并持续优化生产流程&#xff0c;确保产品质量得到提升和保持稳定。 可追溯性不仅涉及追踪产品来源&#xff0c;还是一种保障数据真实性的手段。与数据安全技术相…

OTP语音芯片和TTS语音芯片的差异性

OTP&#xff08;One-Time Programmable&#xff09;语音芯片和TTS&#xff08;Text-to-Speech&#xff09;语音芯片是两种不同类型的声音处理芯片。OTP主要用于播放预录声音片段&#xff0c;而TTS则根据文本实时生成语音。OTP的灵活性较弱&#xff0c;适用于固定声音输出&#…

组件自定义事件 和 解绑事件

组件自定义事件 和 解绑事件 组件自定义事件 功能&#xff1a;父组件绑定数据&#xff0c;子组件触发事件。&#xff08;父绑子触发&#xff09; 实现步骤&#xff08;前三步在父组件实现&#xff0c;第四步在子组件实现&#xff09;&#xff1a; 第一步&#xff1a;提供事件…

图文结合丨Prometheus+Grafana+GreatSQL性能监控系统搭建指南(下)

一、环境介绍 本文环境&#xff0c;以及本文所采用数据库为GreatSQL 8.0.32-24 $ cat /etc/system-release Red Hat Enterprise Linux Server release 7.9 (Maipo) $ uname -a Linux gip 3.10.0-1160.el7.x86_64 #1 SMP Tue Aug 18 14:50:17 EDT 2020 x86_64 x86_64 x86_64 G…

单电源、轨到轨输入输出、高精度运放MS8551/8552/8554,可替代ADI的8551/8552/8554

MS8551/8552/8554 是输入输出轨到轨的高精度运算放大器&#xff0c;它 有极低的输入失调电压和偏置电流&#xff0c;单电源电压范围为 1.8V 到 5V 。 轨到轨的输入输出范围使 MS8551/8552/8554 可以轻松地放大高 电平和低电平的传感信号。所有特性使得 MS8551/8552/8…