第七章——函数(C++的编程模块)

news2024/11/15 23:38:38

复习函数的基本知识

要使用C++函数,必须完成如下工作:

  • 提供函数定义
  • 提供函数原型
  • 调用函数 

库函数是已经定义和编译好的函数,同时可以使用标准库头文件提供其原型,因此只需要正确地调用这种函数即可。但是创建自己的函数时,必须自行处理上面提到的3个方面。例如

#include<iostream>
using namespace std;

void hello();	//函数的声明

int main()
{
	cout << "主函数将调用自己编写的hello这个函数:" << endl;
	hello();	//函数的调用
	cout << "调用自编函数hello结束" << endl;
	return 0;
}

//函数的声明
void hello() 
{
	cout << "我向大家问好!"<<endl;
}

 

程序按行顺序执行。执行函数hello()时,将暂停main()中的代码;等函数hello()执行完毕之后,继续执行main()中的代码。 

定义函数

可以将函数分为两类:没有返回值的和有返回值的函数。

没有返回值的函数称为void函数,通用格式如下:

void functionName(parameterList)
{
    statement(s)
    return;
}

 其中,parameterList指定了传递给函数的参数类型和数量。

有返回值的函数将生成一个值,并将它返回给调用函数。这种函数的返回类型被声明为返回值的类型。通用格式如下:

typeName functionName(parameterList)
{
    statement(s)
    return value;
}

 对于有返回值的函数,必须使用返回语句,以便将值返回给调用函数。注意返回值的类型不能是数组,但可以是其他任何类型——整数、浮点数、指针、结构、对象

函数在执行返回语句后结束。如果函数包含多条返回语句,则函数在执行遇到的第一条返回语句后结束。例如

int bigger(int a,int b)
{
    if(a > b)
        return a;
    else
        return b;
}

 函数原型和函数调用

看下面一个例子

#include<iostream>
using namespace std;
void cheers(int);		//prototype, no return value
double cube(double x);  //prototype, return a double
int main()
{
	cheers(5);			//function call
	cout << "Give me a number: ";
	double side;
	cin >> side;
	double volume = cube(side);   //function call
	cout << "A " << side << "-foot cube has a volume of ";
	cout << volume << " cubic feet" << endl;
	cheers(cube(2));
	return 0;
}

void cheers(int n)
{
	for (int i = 0; i < n; i++)
	{
		cout << "Cheers! ";
	}
	cout << endl;
}
double cube(double x)
{
	return x * x * x;
}

 

1.为什么需要原型?

原型描述了函数到编译器的接口,也就是说它将函数返回值的类型(如果有的话)以及参数的类型和参数的数量告诉编译器。

函数原型是怎样影响下面这条语句的? 

double volume = cube(side);

 对于上面这条语句,函数原型告诉编译器,cube()有一个double参数。如果程序没有提供这样的参数,原型将让编译器能够捕获这种错误;其次,cube()函数完成计算后,将把返回值放在指定的位置,然后调用函数(这里是main()函数)将从这个指定位置取得返回值。由于原型指出了cube()的返回类型是double,因此编译器知道应该检索多少字节以及如何解释它们。如果没有这些信息,编译器将只能进行猜测,而编译器是不会这样做的。

2.原型的语法

函数的原型是一条语句,因此必须以分号结束。最简单的方法就是 复制函数定义中的函数头并添加分号。(函数原型不要求提供变量名,有类型列表就行了;下面这两种都是正确的)

void hell(int);
void hell(int x);

3.原型的功能

原型确保以下几点:

  • 编译器正确处理函数返回值
  • 编译器检查使用的参数数目是否正确
  • 编译器检查使用的参数类型是否正确;如果不正确,则在能力范围内转换为正确的类型 

函数参数和按值传递 

 C++通常按值传递参数,这意味着将数值参数传递给函数,而后者将其赋给一个新的变量。

用于接收传递值的变量被称为形参,传递给函数的值被称为实参。

 在函数中声名的变量(包括参数)是该函数私有的。在函数被调用时,计算机将为这些变量分配内存,在函数结束时,计算机将释放这些变量使用的内存,这样的变量被称为局部变量。

如果在main()中声明了一个名为x的变量,同时在另一个函数中也声明了一个名为x的变量,则它们将是两个完全不同的、毫无联系的变量

多个参数

 函数可以有多个参数,在调用函数时,只需使用逗号将这些参数分开即可:

n_chars('R',50);

 上述函数调用将两个参数传递给函数n_chars()

同样,在定义函数时,也在函数头中使用由逗号分隔的参数声名列表

void n_chars(char c,int n)  //two arguments

该函数头指出,函数n_chars()接受一个char参数和一个int参数,必须分别指定每个参数的类型,不能像声明常规变量那样,将声明组合在一起。

函数和数组

函数是处理复杂类型(如数组、结构)的关键,下面学习如何将数组和函数结合在一起

假设现在要计算一个数组中所有元素的和,我们可以使用for循环逐个遍历相加即可,这样没换一个数字都要进行相应的修改。这里我们写出一个统一的接口,让对于不同的数组修改的尽可能少,不必每次都编写新的循环。

#include<iostream>
using namespace std;
const int ArSize = 8;
int sum_arr(int arr[], int n);
int main()
{
	int cookies[ArSize] = { 1,2,4,8,16,32,64,128 };
	int sum = sum_arr(cookies, ArSize);
	cout << "Total cookies: " << sum << endl;
	return 0;
}
int sum_arr(int arr[], int n)
{
	int total = 0;
	for (int i = 0; i < n; i++)
	{
		total = total + arr[i];
	}
	return total;
}

 

 我们看函数头

int sum_arr(int arr[], int n)

 方括号指出arr是一个数组,方括号为空则表示可以将任何长度的数组传递给该函数。但实际情况并不是这样:arr实际上并不是数组,而是一个指针。(在编写函数的其余部分时,可以将arr看作是数组)

函数如何使用指针来处理数组

前面介绍过,C++将数组名解释为其第一个元素的地址

cookies == &cookies[0]

 数组声明使用数组名来标记存储位置,对数组名使用sizeof将得到整个数组的长度(以字节为单位),将取地址运算符&用于数组名时,将返回整个数组的地址

在函数调用:

int sum = sum_arr(cookies, ArSize);

其中cookies是数组名,而根据C++规则,cookies是其第一个元素的地址,因此函数传递的是地址。由于数组的元素的类型为int,因此cookies的类型必须是int指针,即int*。这表明正确的函数头应该是这样的

int sum_arr(int *arr,int n)

这证明 int *arr和int arr[ ]这两个函数头都是正确的。因为在C++中,当且仅当用于函数头或函数原型中,它们两者的含义才是相同的,都意味着arr是一个int指针。在其他的上下文中,                   int * arr和int arr[ ]的含义是不同的。 

 我们可以看到,上述程序并没有将数组内容传递给函数,而是将数组的位置(地址)、包含的元素种类(类型)以及元素数目(变量n)传递给函数,有了这些信息后,函数便可以使用原来的数组。

传递常规变量时,函数将使用该变量的拷贝;但传递数组时,函数将使用原来的数组

 将数组地址作为参数可以节省复制整个数组所需的时间和内存。

指针和const

可以用两种不同的方式将const关键字用于指针。

第一种方法是让指针指向一个常量对象,这样可以防止使用该指针来修改所指向的值;

int age = 39;
const int* pt = &age;

该声明指出,pt指向一个const int(39),因此不能使用pt来修改这个值

*pt += 1;      //INVALID
cin >> *pt;    //INVALID

pt的声明并不意味着它指向的值实际上就是一个常量,而只意味着对pt而言,这个值是常量。(例如pt指向age,而age不是const,可以直接通过age变量来修改age的值,但是不能使用pt指针来修改它)

*pt = 20; //INVALID
age = 20;

在这里注意又有两种情况,以前我们总是将常规变量的地址赋给常规指针,而这里将常规变量的地址赋给指向const的指针(因此还有两种情况:将const变量的地址赋给指向const的指针、将const变量的地址赋给常规指针。其实只有第一种可行,第二种是不可行的

const float g_earth = 9.8;
const float *pe = &g_earth;    //VALID

 对于这一种情况,既不能使用g_earth来修改值9.8,也不能使用指针pe来修改。

 尽可能使用const

将指针参数声明为指向常量数据的指针有两条理由:

1)这样可以避免由于无意间修改数据而导致的编程错误

2)使用const使得函数能够处理const和非const实参,否则只能接受非const参数

第二种方法是将指针本身声明为常量,这样可以防止改变指针指向的位置。

int sloth = 3;
int *const finger = &sloth;

 这种声明结构使得finger只能指向sloth,但允许使用finger来修改sloth的值

递归

C++函数有一种有趣的特点——可以自己调用自己(与C语言不同的是,C++不允许main()调用自己),这种功能被称为递归。

 如果递归函数调用自己,则被调用的函数也将调用自己,这将无限循环下去,除非代码中包含终止调用链的内容。通常的方法将递归调用放在if语句中,如下面的一个递归函数

void recurs(argumentlist)
{
    statements1
    if(test)
        recurs(arguments2)
    statements2
}

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

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

相关文章

直线导轨的基本结构

正常来讲&#xff0c;直线导轨的结构组成比较简单&#xff0c;主要都是由&#xff1a;导轨→滑块→轴承&#xff0c;这3大部分组成&#xff0c;其最突出的特点是具有互换性和自动调心能力&#xff0c;所有方向都有高刚性&#xff0c;以下就来详细的说一下直线导轨的基本结构吧&…

13-Vue长列表优化 vue-virtual-scroller

回答&#xff1a; 在大型的企业级项目中经常要渲染大量的数据&#xff0c;这种长列表是一个很普遍的场景&#xff0c;当列表内容越来越多就会导致页面滑动卡顿、白屏、数据渲染较慢的问题&#xff1b; 这种情况主要发生在小程序、移动端或者后台管理的页面当中&#xff1b; 通常…

K 个一组翻转链表——力扣25

题目描述 方法一&#xff09;模拟 class Solution{ public:pair<ListNode*, ListNode*> myReverse(ListNode* head, ListNode* tail){ListNode* prev tail->next;ListNode* p head;while(prev!tail){ListNode* nex p->next;p->next prev;prev p;p nex; }r…

基于STM32+SHT30设计的环境温度与湿度检测系统(IIC模拟时序)

一、项目功能介绍 当前介绍基于STM32F103ZCT6芯片设计的环境温度与湿度检测系统设计过程。当前系统通过SHT30温湿度传感器采集环境温度和湿度数据&#xff0c;并通过模拟IIC时序协议将数据传输到STM32芯片上。然后&#xff0c;STM32芯片通过处理这些数据并将它们显示在0.91寸O…

微信小程序和微信公众号之前的关系

微信小程序和公众号是相互独立的&#xff0c;是2个不同的东西 在公众号上关联小程序后&#xff0c;可以在公众号的底部菜单栏点击直接进入小程序。2者的用户可以互通&#xff0c;其他就没别的

【C++】STL——string类对象的修改操作、string类对象修改函数的介绍和使用、push_back、append、operator+

文章目录 1.string类对象的修改操作&#xff08;1&#xff09;push_back&#xff08;尾插单个字符&#xff09;&#xff08;2&#xff09;append&#xff08;尾插字符串&#xff09;&#xff08;3&#xff09;operator&#xff08;尾插字符串str&#xff09;&#xff08;4&…

linux基础 - SHELL1

目录 一、基础 1.test测试语句 2.逻辑运算 3.类型判断 4.数值判断 5.字符串比较 6.计算表达式 二、shell数组 1.单行定义 2.多行定义 3.单元素定义 4.查看元素 5.基于内容找索引 6.增删改 三、shell高级 1.重定向 2.其他符号 3.信息符号 4.if语句 5.else …

unity --- DoTween插件安装与使用

目录 一.简介 1.1 插件下载 1.2 官方文档 二.导入和设置 三.示例代码 1 循环动画&#xff1a;SetLoops(-1 , loopType ) 2. 动画循环次数&#xff1a;SetLoops( count ) 3. 动画事件监听与回调&#xff1a;OnStart \ OnComplete 4. 动画延迟回调&#xff1a;SetDelay …

Java8特性:函数式接口与Stream流

1、 函数式接口 &#xff08;1&#xff09;四大函数式接口 &#xff08;2&#xff09;代码01 //void accept(T t);消费型接口&#xff0c;有参数&#xff0c;无返回 Consumer<String> consumer t->{System.out.println(t); }; consumer.accept("a"); //…

DAY42:动态规划(二)斐波那契数列+爬楼梯+最小花费爬楼梯

文章目录 509.斐波那契数列思路&#xff1a;动规五步确定dp数组和数组下标含义递推公式DP数组初始化遍历顺序打印DP数组 完整版debug测试 空间复杂度优化版优化思路 70.爬楼梯思路DP数组的含义以及下标含义递推公式DP数组初始化遍历顺序打印DP数组 完整版debug测试 空间复杂度优…

10.24UEC++、事件

1. 类内部声明事件类型&#xff1a; 生成actor 绑定到事件上 实现&#xff1a; 移除&#xff1a;

使用docker安装redis并使用redis deskTop manager连接

使用docker安装redis 提前准备环境 linux、 docker环境 # 使用命令查看docker环境是否正常docker images(任何一个docker命令就可以&#xff0c;不一定必须这个) 下载redis镜像 # 使用命令下载镜像文件docker pull redis 确认镜像 # 使用命令查看镜像docker images使用redis d…

功能测试的测试流程是什么样的?

概述 本流程是描述软件功能自动化测试过程中的步骤、内容与方法&#xff0c;明确各阶段的职责、活动与产出物。 流程活动图 活动说明 测试计划&#xff08;可选&#xff09; 与以前的测试计划过程一致&#xff0c;只是在原来的测试计划中&#xff0c;添加对项目实施自动化测试…

Qt窗体全屏与复原,子窗体全屏与复原

QT QWidget窗体全屏时&#xff0c;全屏与最大化的区别时最大化有标题框全屏没有框&#xff0c;可以使用自带的函数&#xff1a; showFullScreen(); 复原时也有专门的函数&#xff1a; showNormal(); 如果我想在点击最大化按钮时&#xff0c;让窗体全屏。这个时候最好有线程…

学好工作汇报,你离升职加薪也就不远了

在职场中&#xff0c;工作汇报是展示你的价值和成果的重要环节。无论你是想要升职还是加薪&#xff0c;一个出色的工作汇报能够给你带来巨大的竞争优势。然而&#xff0c;许多人对于如何进行有效的工作汇报感到困惑。在本文中&#xff0c;我们将向您介绍一些关键技巧和策略&…

路径规划算法:基于骑手优化优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于骑手优化优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于骑手优化优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化…

Linux学习之vim在光标所在处写入内容,保存退出

vim insertTest使用vim打开一个叫做insertTest新文件。 输入命令之后&#xff0c;按下Enter&#xff08;回车键&#xff09;&#xff0c;就可以进入下边的操作页面。 按i在当前光标位置处&#xff0c;就可以插入内容了。 接下来写入内容跟记事本里边是差不多的。 按一下…

【Spring】Spring基础知识 Java开发必看

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;目前专攻C/C、Python、Java等方向&#xff0c;一个正在慢慢前行的普通人。 &#x1f3c0;系列专栏&#xff1a;陈童学的日记 &#x1f4a1;其他专栏&#xff1a;CSTL、蓝桥杯&am…

vue拼接html中onclick的触发方式,模版字符串拼接点击事件在vue项目中不生效问题

模版字符串拼接点击事件在vue项目中不生效问题 下面的点击事件没有任何效果&#xff0c;但是如果换成onclick绑定事件则会提示没有该方法。主要原因是&#xff1a; 模版字符串中拼接的html片段中的方法调不到vue中this.methods里的东西&#xff0c;因为methods里的代码是编译…

物体识别(香蕉)

✨✨✨ 感谢优秀的你打开了小白的文章 “希望在看文章的你今天又进步了一点点&#xff0c;生活更加美好&#xff01;”&#x1f308;&#x1f308;&#x1f308; 目录 1.数据集下载 2.数据集导入 3.结果 1.数据集下载 包含所有图像和CSV标签文件的香蕉检测数据集可以直接从互…