C/C++语言基础--函数基础(函数定义、调用、生命周期、递归)

news2024/11/15 10:23:18

本专栏目的

更新C/C++的基础语法,包括C++的一些新特性

前言

  • 函数是语言的基本组成部分,也是面向对象编程的基石,他体现了封装的思想,代码的复用的功能。
  • 欢迎点赞 + 收藏 + 关注,本人将会持续更新

文章目录

  • 函数
    • 什么是函数?
    • 函数分类
    • 函数的定义
    • 函数的调用
    • 函数声明/多文件编程
  • 变量的作用域和生命周期
  • 函数递归

函数

什么是函数?

  • 是一个大型程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。
  • 是构成程序的基本模块,封装思想,可以实现代码复用

为什么要使用函数?

  • 可以省去编写重复代码的苦闷
  • 可以让程序模块化,提高代码可读性
  • 方便后期修改、完善
  • 隐藏了实现的细节
  • 下图是在一个入口函数main下调用的函数

在这里插入图片描述

函数分类

从用户角度

  • 标准函数,即库函数
    • C语言封装好的函数,非常丰富
    • 例如:
      • —输入/输出:scanf(),printf(),rand()…
      • —数学计算:sqrt(),abs(),sin()…
  • 自定义函数
    • 自己编写的用来完成特定任务的代码块

从函数形式

  • 无参函数:getchar()
  • 有参函数:putchar(‘a’)

函数的定义

和变量一样,要想使用一个函数,定义是不可缺少的,函数定义有4个要素:返回类型,函数名,形参和函数体,参数列表和返回类型对应着输入输出,函数名是函数的标识,而函数体是一段可执行的代码块,实现特定的算法或功能。

在这里插入图片描述

如果我们要写一个功能,用来获取两个数中的最大值,我们可能这样写。

a > b ? a : b;  // 三目表达式

这样写,是很好的,但是当我们需要多次使用的时候,代码将变得不清晰,此时我们可以用一个函数对这个功能进行封装,把结果进行返回。

int max(int a,int b)
{
    return a>b?a:b;
}

然后使用函数名加上实参即可调用该函数,然后可以使用一个变量接受函数的返回值。

int res = max(2,5);

在这个过程中,我们提到了参数这个概念:

  • 函数定义时参数列表中的叫做形参 形参可以接受实参传过来的值
  • 函数调用时参数列表中的叫做实参 实参会赋值给形参,让函数内部进行操作
  • 注意:
    • 函数的传参过程,只是单纯的值传递
    • 所以在函数内部改变形参的值,不会影响到实参
    • 以下代码,在函数内部改变了形参,但是实参确没有变化 ↓
void foo(int num)
{
    num++;
    printf(num);
}
int main()
{
    int a =3;
	foo(a);
	printf(a);
    return 0;
}
output:num:4  a:3 

打个形象的比方,这是角色和演员的关系。

函数定义时列表中的参数称为形参,是“剧本角色”,而函数调用时传递进来的参数称为实参,是“演员”,函数执行的过程就是演戏的过程。

程序刚开始执行的时候,编译器并不为形参分配存储空间,因为它只是个角色,不是实体,一直要到函数调用时,编译器为形参分配存储空间,并将实参的值复制给形参结合。可知,在foo(a)语句调用前,num不是真正的程序变量,一直到foo函数被调用,num才被创建,并用a为其赋值,找这种情况下,在函数内对num的处理并不影响a,这类似于“某个演员扮演的角色在戏中受伤,并不是说演员真的受伤了”,而且,在函数执行结束返回时,创建的形参被撤销,这类似于“戏演完了,剧中角色自然也就停止了”。

函数的调用

函数调用的一般形式为:函数名(实参列表)

  • 实参可以是常数、变量、表达式等,多个实参用逗号,分隔

    函数的调用姿势有多种:

// 作为表达式的每一项
int res = add(5, 3) + 6;
// 作为其他函数的参数
printf("%d", add(5, 3));
// 其他…………

注意:

  • 调用函数时不要忘记括号,尤其是无参函数
  • 调用函数时,实参与形参个数必须一致,类型尽量保持一致
  • 调用库函数时,应在程序头部用#incldue包含相关头文件
  • 函数调用不能作为左值

函数声明/多文件编程

  • 函数声明会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。

函数声明包括以下几个部分

返回类型 函数名(参数列表);

针对前面自定义的max函数,应该这样声明

int max(int a,int b);

在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明:

int max(int,int);
  • 函数声明必须和定义时的函数前面一致
  • 函数原型的作用是告诉编译器与该函数有关的信息,让编译器知道函数的存在,以及存在的形式,即使函数暂时没有定义,也不会出错。

变量的作用域和生命周期

根据变量的作用域不同,可分为局部变量和全局变量两种

  • 变量的作用域

    • 变量在那一个范围有用,遵循就近原则,如:

    • int a = 10;
      
      int add()
      {
          int a = 20;
          int b = 10;
          
          return a + b;   // 此时 a = 20,不等于10
      }
      
  • 局部变量

    • 函数内部或者某个控制块的内部定义的变量,如:

    • // 函数内部
      void test()  
      {
          int a;    
      } // 这里后,a释放
      
      // 快内部:
      {
          int a = 10;
      }// 这里后,释放
      
    • 作用域:函数内部或控制块内部

    • 生命周期:从定义开始,到函数结束或者控制块结束

  • 全局变量

    • 在函数外部定义的变量
    • 作用域:在同一文件中,所有函数都可以引用全局变量
    • 生命周期:从程序运行开始,到程序结束

根据变量的生命周期不同,可分为静态变量和动态变量

在这里插入图片描述

自动(动态)变量

  • 指的是局部变量,具体来说即是在控制流进入变量作用域时系统自动为其分配存储空间,并在离开作用域时释放空间的一类变量。

静态变量

  • 1,在程序执行的整个过程中,都不释放内存
  • 2,静态变量在编译时初始化,仅初始化一次如果没有初始值,将自动初始化为0
  • 3,作用域:定义的函数内部

寄存器变量(忘记现查也行)

建议编译器将变量存储到CPU寄存器中

  • 1,编译器有自己的处理方式,不一定会接受你的建议
  • 2,一旦接受建议,把变量存到了寄存器里面,那么则不能对变量取地址(&)
  • 3,只有局部变量和形参可以作为register变量,全局变量不行,静态变量也不行

static和extern改变函数和变量的作用域

  • 全局变量默认就是extern类型的,而且作用范围是整个项目;如果手动加上static,则表示让全局变量只能在当前文件中使用。(与static对应的就是extren,全局变量默认是extren)

    /*test.c*/
    int number = 520;
    
    /*main.c*/
    #include<stdio.h>
    extern int number;		//声明使用外部变量
    int main() 
    {	
    	printf("%d ", number);
    }
    

    如果在test.c中的变量定义前面加上static,则会产生如下错误

    1>main.obj : error LNK2001: 无法解析的外部符号 _number
    1>F:\MyCode\2048\Debug\2048.exe : fatal error LNK1120: 1 个无法解析的外部命令
    
  • 对于函数来说也是如此

    /*test.c*/
    int get()
    {
    	return 566;
    }
    
    /*main.c*/
    extern int get();   // 生命使用外部函数
    int main() 
    {	
    	printf("%d", get());
    }
    

函数递归

递归也是一种函数调用,只不过是函数自己调用自己,主要解决重复做一样的事情

  • 使用递归必须要满足的两个条件,写递归的时候必须要想清楚的:

    • 先写终止条件

    • 要有递归公式。

  • 递归和循环的关系

    • 递归和循环存在很多关系。理论上讲,所有的循环都可以转化成递归
    • 有转换套路,但是一般不用,哪怕在算法比赛中,也不推荐,因为花时间多,难转换,尤其在复杂递归中。
  • 递归的优缺点

    • 递归的优点是简化程序设计,结构简洁清晰,容易编程,可读性强,容易理解。
    • 递归的缺点也很明显:速度慢,运行效率低,对存储空间的占用比循环多。而循环效率高,但是很多时候,不用转化,哪怕在算法比赛中也允许使用递归

通过一个简单的程序了解递归

int foo(int n)
{
    if(n == 0)   // 终止条件
    {
        return 0;
	}
    printf("I love you\n");  // 递推公式
    foo(n-1);
}

递归求斐波拉契数列

int fibonacci(int n)
{
    if(n==0||n==1)   // 终止条件
    {
        return 1;
    }else
    {
        return fibonacci(n - 1) + fibonacci(n-2);  // 递推公式
    }
}

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

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

相关文章

彩漩科技亮相第一届人工智能教育应用论坛,荣获AI教育科技产品TOP30奖项

近日&#xff0c;由中国教育发展战略学会人工智能与机器人教育专业委员会指导&#xff0c;北京教育信息化产业联盟主办的第一届人工智能教育应用论坛暨 AI 教育科技成果展在北京隆重举办。本次活动以“ AI 强校大时代 —— 用新质生产力打造金钥匙强校”为主题&#xff0c;汇聚…

vector 常见函数

目录 一.vector 构造函数 二 . Iterators 迭代器&#xff08;random access iterator&#xff09; 三.Capacity: 空间 3.1 resize 3.2 reserve 四.Element access: 元素访问 方式 4.1 operator[] 类似于数组的 [] 4.2 front 和back 五.Modifiers: 六.vector 的 二…

数据结构(单向链表)

单向链表代码 #ifndef _LINK_H_#define _LINK_H_typedef int DataType;typedef struct node {DataType data;struct node *pnext; }Link_Node_t;typedef struct link {Link_Node_t *phead;int clen; }Link_t;extern Link_t *link_creat(); extern int push_link_head(Link_t *…

智慧公厕技术应用、系统架构、应用功能有哪些?@卓振思众

智慧公厕的标准涵盖了多个方面&#xff0c;包括技术应用、系统架构、应用功能以及环保节能等。以下是【卓振思众】整理的一些标准要点&#xff1a; 技术应用‌物联网技术‌&#xff1a;通过无线传感器、监控设备等实时采集公厕内部环境数据。‌大数据与云计算‌&#xff1a;对数…

2157. 优秀的拆分(power)

代码 #include<bits/stdc.h> using namespace std; int a[10001]; int main() {int n,t1,k0;bool flagfalse;cin>>n;if(n%21) {cout<<-1;return 0;}while(n>0){if(n%21){k;a[k]t; }nn/2;tt*2;}if(k>1) {flagtrue;for(int ik;i>1;i--)cout<&l…

lit-llama代码解析

https://github.com/Lightning-AI/lit-llama/blob/main/README.md 下载的时候会报错误&#xff0c;因为网不行&#xff0c;一种方法就是多次尝试&#xff0c;另一种方法是终端连上代理下载 pycharm连接hugging face等网站_hugging face怎么连接-CSDN博客 根据指引下载权重 下…

springboot,maven多模块开发,子模块获取不到父模块添加的依赖,有多个root模块问题解决

错误示范 我以为放进去然后重载一下就是子模块了 导致后续在外层加的依赖&#xff0c;其article都接收不到 解决方案 需要在父模块的modules注册子模块 修改前后对比 此时子模块也能获取父模块的依赖

DDD设计方法-2-聚合、实体、值对象

前情提要&#xff1a;一共包含 如下六篇文章&#xff08;篇幅精简&#xff0c;快速入门&#xff09; 1、初识DDD 2、聚合、实体、值对象 3、仓储&#xff0c;封装持久化数据 4、端口和适配器 5、领域事件 6、领域服务&#xff0c;实现约定 DDD设计方法-2-聚合、实体、值对象&a…

基于mspm0g3507的智能送药小车(21年电赛f题,openmv寻迹,k210数字识别,并行pid调制)项目实验报告

2024年全国大学生电子设计竞赛&#xff08;TI杯&#xff09; 2024年7月17日 摘要&#xff1a;本项目由微处理器MSPM0G3507&#xff0c;编码器电机驱动&#xff0c;OPENMV、K210视觉处理单元&#xff0c;红外药品检测单元&#xff0c;ZIGBEE无限透传单元&#xff0c;OLED显示&am…

Docker数据卷和Dockerfile

1、什么是Docker数据卷 前言&#xff1a; 在下载的镜像中&#xff0c;我们不能够去改变它内部的一些配置&#xff0c;因为docker的镜像文件是已经配置好的&#xff0c;无法改变&#xff0c;我们只能改变镜像启动后的容器里面的内容&#xff0c;但是又因为&#xff0c;容器本来…

Java框架第四课(对Spring的补充Spring web)

目录 一.Spring web的认识 (1)Spring Web概念 (2)Spring web的特点 (3)Springweb运行的流程 (4)Springweb运行的流程图 二.搭建Spring web 三.自定义处理器类搭建 (1)处理器类配置 (2)处理器类接受请求 (3)获得请求数据 四.拦截器 (1)关于拦截器&#xff1a; (2)拦截器的…

【VMware】麒麟系统网络连接配置

在VMware配置页面点击编辑&#xff0c;进入虚拟网络编辑器将默认的 VMnet0删除&#xff0c;新建网络&#xff0c;设置桥接模式为Intel 打开主机cmd,查看主机IP地址&#xff0c;获取子网掩码&#xff0c;默认网关及DNS服务器 4.在主机寻找可用IP地址&#xff0c;ping不通的为未…

探秘发酵过程:酵母菌如何为白酒赋予不同风味?

在白酒酿造的神秘世界里&#xff0c;发酵过程如同一位隐形的艺术家&#xff0c;用其不同的笔触为白酒勾勒出千变万化的风味。而在这背后&#xff0c;酵母菌作为发酵的主角&#xff0c;发挥着至关重要的作用。今天&#xff0c;就让我们一起探秘发酵过程&#xff0c;了解酵母菌如…

shell 学习笔记:变量、字符串、注释

目录 1. 变量 1.1 定义使用变量 1.2 变量命名规则 1.3 只读变量 1.4 删除变量 1.5 变量类型 1.5.1 字符串变量 1.5.2 整数变量 1.5.3 数组变量 1.5.3.1 整数索引数组 1.5.3.2 关联数组 1.4 环境变量 1.5 特殊变量 2. 字符串 2.1 单引号字符串 2.2 双引…

erlang学习:用OTP构建系统23.12练习题

练习要求 制作一个名为prime_tester_server的gen_server&#xff0c;让它测试给定的数字是否是质数。 你可以使用lib_primes.erl里的is_prime/2函数来处理&#xff08;或者自己实现一个更好的质数测试函 数&#xff09;。把它添加到sellaprime_supervisor.erl的监控树里。 质…

图论(2)

一、度 度统计的是一个节点上又多少条边 度出度入度 出度&#xff1a;统计以该节点为起始点箭头指向外面的边的条数 入度&#xff1a;统计箭头指向该节点的边数 度为1的节点为悬挂节点&#xff0c;边为悬挂边 用矩阵计算节点的度 二、握手定理 比如这里第一个集合里面有三…

ARP协议(原理,特点,报文格式,具体过程),ARP缓存(有效时间,为什么),ARP欺骗(定向断网,成为中间人),RARP简单介绍

目录 ARP协议 引入 介绍 原理 arp请求/响应 特点 报文格式 硬件类型 协议类型 硬件/协议地址长度 op(操作码) 过程 发送请求并处理 返回响应并处理 总结 arp缓存 介绍 arp表项的有效时间 解释 arp欺骗 介绍 定向断网 基于arp的成为中间人的方式 多向…

跟李沐学AI:序列模型

目录 序列数据 自回归模型 马尔可夫假设 潜变量模型 序列模型总结 序列数据 实际中很多数据是时序结构的&#xff0c;如&#xff1a;电影的评价随时间的变化而变化&#xff1a;拿奖后评分上升、电影整体质量提升&#xff0c;人们要求变高。。。等等 除此之外&#xff0c;音…

比特币网络和支付

1. 比特币网络 比特币网络是一个去中心化的点对点网络&#xff0c;节点之间可以直接进行交易。网络上有不同类型的节点。 1.1 比特币网络的节点 比特币网络的节点有两种主要类型&#xff1a;全节点也称为完整节点和简单支付验证&#xff08;Simple Payment Verification,SPV)节…

档案|基于SprinBoot+vue的档案管理系统(源码+数据库+文档)

档案管理系统 基于SprinBootvue的档案管理系统 一、前言 二、系统设计 三、系统功能设计 管理员功能模块实现 学生功能模块实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农…