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

零基础学C++,函数篇~

C++基础学习(DAY_06)

  • 函数
    • 1. 函数的定义与使用
    • 2. 函数参数传递
    • 3. 变量的声明周期
    • 4. 函数的其他特性
    • 5. 函数的嵌套与递归

函数

1. 函数的定义与使用

​ 在设计程序时,如果一段代码重复进行某种操作或者完成一个特定的功能,就应该将这些代码封装组织成函数,以实现代码复用。

  • 定义:函数类型 函数名(形式参数列表)
    {
    函数体中的语句组;
    }
    以上,函数由函数类型、函数名、函数参数组成,这部分称为函数头。函数名遵守标识符命名规则。
    大括号称为函数体,里面写函数执行的具体代码。
    函数定义时只能在全局范围定义,不能在局部定义,不能在函数内部再定义函数。
  • 函数类型与返回值:
    函数可以有返回值,也可以没有。
    函数类型就是指函数的返回值类型,可以是C++支持的任意类型。
    函数返回值是由return语句给出的。
    当函数不需要返回值的时候,返回值类型要用void空类型指定
  • 函数参数:
    形参(形式参数):定义函数时括号中的参数
    实参(实际参数):调用函数时括号中的参数
  • 函数调用:函数名(实参);

函数声明:
声明一个函数,就是写出函数的原型(函数头部),但没有写出函数体(实现部分)。主要用于告诉编译器,它的实现放在其他地方。
如果函数没有声明,调用的时候就要按照顺序书写,就要在前面定义好函数,后面才能调用函数。否则就会找不到函数报错。

2. 函数参数传递

函数实参可以是常量、变量、表达式。在函数调用时,系统为形参分配内存空间并且将实参的值传到形参的空间中。
根据函数的给你需要,可以选择三种不同的传参方式:值传递、指针传递、引用传递
1)值传递:实参的值传递给形参

2)指针传递:指针作为函数的参数

3)引用传递:引用传递本质上也是传递的地址
在这里插入图片描述

//值传递:用来交换主调函数中俩个变量的值。
void swap(int x,int y)
{cout << "&x=" << &x << " &y=" << &y << endl;int temp = x;x = y;y = temp;
}void test02()
{int n1 = 10, n2 = 20;cout << "&n1=" << &n1 << " &n2=" << &n2 << endl;swap(n1,n2);//交换n1,n2的值cout << "调用swap函数之后:" << endl;cout << n1 << " " << n2;//交换失败,原因:值传递只是把n1,n2实参的值传递给形参x和y,函数内部交换的是x、y,不会对n1、n2进行操作
}

在这里插入图片描述

//用指针传递,交换主调函数中俩个变量的值。
void swap_p(int* x,int* y)
{cout << "x=" << x << " y=" << y << endl;int temp = *x;*x = *y;*y = temp;
}void test03()
{int n1 = 10, n2 = 20;cout << "&n1=" << &n1 << " &n2=" << &n2 << endl;swap_p(&n1, &n2);//交换n1,n2的值cout << "调用swap_p函数之后:" << endl;cout << n1 << " " << n2;//交换成功,因为指针传递的是地址,函数内部操作的是n1和n2的地址。
}//用引用传递,交换主调函数中俩个变量的值。
void swap_r(int& x, int& y)
{cout << "&x=" << &x << " &y=" << &y << endl;int temp = x;x = y;y = temp;
}void test04()
{int n1 = 10, n2 = 20;cout << "&n1=" << &n1 << " &n2=" << &n2 << endl;swap_r(n1, n2);//交换n1,n2的值cout << "调用swap_r函数之后:" << endl;cout << n1 << " " << n2;//交换成功
}

引用传递与值传递用法上的区别:

  • 引用传递:调用函数时,实参只能是变量。主要用在复杂数据类型(自定义类型)传递时,可以节省空间和时间。
  • 值传递:调用函数时,实参可以使变量、常量或者表达式。
//俩个函数,分别是值传递和引用传递,计算圆的面积
double area1(double radius)
{double a = 3.14 * radius * radius;return a;
}
double area2(double& radius)
{double a = 3.14 * radius * radius;return a;
}void test05()
{double r = 10;//变量cout << "值传递:(变量)" << area1(r) << endl;cout<<"值传递:(常量)"<< area1(100) << endl;cout<<"值传递:(表达式)" << area1(r*2) << endl;cout << "引用传递:(变量)" << area2(r) << endl;//以下不支持//cout << "引用传递:(常量)" << area2(100) << endl;//cout << "引用传递:(表达式)" << area2(r * 2) << endl;
}

数组作为函数参数:
数组名实际上是数组的首元素地址,所以数组作为函数参数传递其实传递的是首元素地址,无法将整个数组传递。
同样如果想返回一个数组,也是返回一个指针。

//编写一个函数,将整型数组中的元素按照相反的顺序来存放,即翻转数组
void my_reverse(int* p,int n)//第一个元素传递的是数组的首地址,第二个参数传递的是数组的元素个数;仅凭数组的首地址,无法获取元素个数
{//用俩个指针实现交换,分别指向第一个元素和最后一个元素,然后一个指针往后走,一个指针往前走,直到他俩相遇。int* i, * j, temp;for (i = p, j = p + n - 1;i < j;i++, j--){temp = *i;*i = *j;*j = temp;}
}void test06()
{int a[10] = { 0,1,2,3,4,5,6,7,8,9 };int n = sizeof(a)/sizeof(int);my_reverse(a, n);for (size_t i = 0; i < n; i++){cout << a[i] << " ";}
}

3. 变量的声明周期

每个标识符都有确定的作用域,决定他们在什么范围内能被访问。
整个作用域又决定了他们的生命周期。

栈内存中的局部变量在进入它的作用域时在内存中创建,在离开它的作用域后,被编译器自动回收释放。
堆内存中的变量需要程序员自己管理,它的生命周期由程序员决定。

  • 局部变量和全局变量:
    函数内定义的变量是局部变量,他只在函数范围内有效。
    在函数外定义的变量是全局变量,它在整个文件内有效。
    全局变量在当前文件的任何函数中都可以访问。
    如果我们在不同的作用域下定义了多个同名变量的话,访问规则是优先访问作用域最小的那个(离你最近的那个)
    如果要在某个作用域下访问同名的全局变量,那么可以在变量前面加域运算符::

变量的存储方式:
1)静态存储
静态存储是指在程序运行期间,给变量分配的内存一直有效。
定义:static 数据类型 变量名;
2)动态存储
是指在程序运行期间,根据需要动态管理变量的内存空间。如局部变量,当执行到该函数时,就为局部变量分配空间,
当离开函数时,局部变量就被释放。下次在执行到该函数时,再重新分配。这些是通过编译器在栈内存自动完成的。
堆内存也属于动态存储,但是变量的生命周期由程序员自己决定。

4. 函数的其他特性

内联函数==(inline==):
就是将函数嵌入到可执行程序中,而不需要去调用的函数。
有一些函数需要频繁调用而且函数内部的代码规模很小,逻辑简单。为了减少函数调用的开销(包括寻找函数地址,传参…),
可以将这种函数设置为内联函数,相当于将他嵌入到函数的调用处。
注意:只有简单的函数才能成为内联函数,如果函数复杂,含有循环分支等结构,即使定义为内联函数,编译器也会当做普通函数处理。
定义:
inline 函数类型 函数名(形参列表);

  • 函数重载:
    可以定义多个同名,只要参数不同就可以(参数的个数或者类型不同即可)。
    调用时,编译器会根据实际参数来选择匹配的函数去调用。这称为重载。
  • 带默认参数值的函数:
    函数定义时,可以预先给某些参数指定默认值,如果函数调用时没有给出实际参数,就会采用默认值,如果给出实参就会替换默认值。
    注意:默认值必须从右往左依次给出。如果某个参数有默认值,那么它右边的所有参数都必须有默认值。
    错误示范:int max(int a,int b=10,int c);

5. 函数的嵌套与递归

函数的嵌套指的是函数调用函数,是函数的嵌套。函数定义是不能嵌套。

递归:递归就是函数调用自身,一个函数可以通过直接或者间接的方式来调用自己实现递归。
写递归函数的关键点:
1)明确函数的功能
明确函数接收什么参数,返回什么结果,完成什么逻辑。例如阶乘,功能就是接收1个整数n,计算并返回n的阶乘。
2)确定递归的终止条件
递归函数必须有终止条件,否则会陷入无线递归,最终会导致栈内存被沾满,叫做栈溢出。
终止条件是递归函数停止调用自身的条件,通常对应着最简单的情况。
3)找出递归关系
递归关系是如何将一个大问题分解为多个小问题的过程。在阶乘例子中,n的阶乘是个大问题,分解为小问题,就是n的阶乘=n*(n-1)的阶乘
4)要确保递归调用的参数能逐渐逼近终止条件
每次递归调用时,传递给递归函数的参数要向终止条件逼近,阶乘例子中,每次调用时,n都-1,保证了最终达到终止条件
5)处理递归的返回值
在递归函数中,要将每次调用的返回值用于当前问题的求解。


//用递归的方式求n的阶层
long power(int n)
{if(n==1 || n==0){return 1;}return n * power(n - 1);
}void test13()
{cout << "请输入一个整数,小于10" << endl;int n;cin >> n;cout << "它的阶乘=" << power(n) << endl;
}
http://www.xdnf.cn/news/19431.html

相关文章:

  • Visual Studio内置环境变量有哪些
  • MQTT 连接建立与断开流程详解(一)
  • Redission 实现延迟队列
  • Verilog 硬件描述语言自学——重温数电之典型组合逻辑电路
  • 基于 Spring Boot3 的ZKmall开源商城分层架构实践:打造高效可扩展的 Java 电商系统
  • 大语言模型的“可解释性”探究——李宏毅大模型2025第三讲笔记
  • Linux kernel 多核启动
  • Tomcat 企业级运维实战系列(六):综合项目实战:Java 前后端分离架构部署
  • 〔从零搭建〕数据中枢平台部署指南
  • 汽车加气站操作工证考试的复习重点是什么?
  • 如何取得专案/设计/设定/物件的属性
  • ETCD学习笔记
  • 手表--带屏幕音响-时间制切换12/24小时
  • 从零开始学习单片机18
  • 《云原生架构从崩溃失控到稳定自愈的实践方案》
  • 消费 $83,用Claude 实现临床护理系统记录单(所见即所得版)
  • C++三方服务异步拉起
  • MySQL函数 - String函数
  • Google Protobuf初体验
  • 深层语义在自然语言处理中的理论框架与技术融合研究
  • 使用电脑操作Android11手机,连接步骤
  • Python爬虫实战:研究统计学方法,构建电商平台数据分析系统
  • 面经分享--小米Java一面
  • 具有类人先验知识的 Affordance-觉察机器人灵巧抓取
  • STM32 之GP2Y1014AU0F的应用--基于RTOS的环境
  • 老题新解|不与最大数相同的数字之和
  • PCB 局部厚铜工艺:技术升级与新兴场景应用,猎板加工亮点
  • 同步/异步日志库
  • 响应式编程框架Reactor【4】
  • Web 聊天室消息加解密方案详解