Chapter 6: Functions

本文介绍了局部对象的概念及其生命周期,并详细阐述了函数参数的三种传递方式:按值传递、按指针传递和按引用传递。同时,还讨论了如何通过额外的引用形参返回多个参数,以及main函数如何接收命令行参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 什么是局部对象? 它的的生命周期是怎样的?
    形参和在函数体内部定义的变量统称为局部变量, 仅在函数作用域内可见. 其生命周期依赖于定义的方式. 局部对象有两种:
    1. 自动对象
      当函数执行到对象定义语句(函数内部)时创建该对象, 当执行到对象所在块(其实是对象名的作用域)末尾时销毁该对象.
    2. 局部静态对象
      当函数执行到对象定义语句(函数内部)时创建该对象, 直到程序终止时销毁该对象.

      形参是自动对象, 其在函数开始时定义, 函数结束时形参被销毁.


  • 函数参数传递的方式有哪些?
    函数传递参数的方式有三种: 按值传递 按指针传递 按引用传递
    • 按值传递
      按值传递即直接将要传递给函数的对象的值传递给函数的形参. 这里的直接事实上包含了拷贝的过程.
int i = 20; // 要传递给函数function的实参
void function(int num)
{
    cout << num << endl;
}
function(i); // 调用函数
//----------------真实的传值过程-----------------------
int temp = i; // temp是临时对象, 获得i的拷贝值
num = temp;

    • 按指针传递
      按指针传递是将要传递给函数形参的对象的地址传给形参.
      为什么要建立按指针传递的机制呢? 可以想象一下, 如果我们有一个非常大的类对象要传递给函数, 那么复制一个这样的类对象并传递给函数将会占用很多内存, 并且当我们想对原类对象做一些改变时, 由于我们在函数中处理的是原类对象的一个拷贝, 原类对象是不会有任何改变的. 如果将类对象的地址传给函数, 一方面不用占用很大的内存, 另一方面也可以直接改变原类对象的内容. 考虑到按指针传递时有时候我们并不想改变指针指向的内容, 所以又建立了const机制.
int i = 20; // 要传递给函数function的实参

void function(int *num)
{
    cout << num << endl;
}
function(&i); // 调用

传递数组时实际上传递的是指针.


    • 按引用传递
      C++建立了类似于按指针传递的引用传递机制. 只不过传递的不是指向对象的指针而是绑定对象的引用.

C++中尽量使用按引用传递的机制.


  • 如果我想返回多个参数怎么办?
    当需函数要返回多个参数时, 可以创建额外的引用形参, 将希望从函数中获取的信息通过形参传递出来.
int i = 3, j = 2;
int sum = 0;
// 需要函数返回积与和
// 积通过返回值获取
// 和通过额外形参获取
int function(int num1, int num2, int &refSum)
{
    refSum = num1 + num2;
    return (i * j);
}

int mul = function(i, j, sum);
// 此时mul = 6 sum = 5

  • main函数有没有参数? 怎么给main函数传递参数?
    事实上main函数可以带有形参:
int main(int argc, char *argv[])
{
    ......
}

第二个形参argv是一个C风格的字符串指针, 第一个形参是数组中argv[]中字符串的个数. argv的第一个字符串必须指向程序名或者空字符.
通过下面命令行语句给main函数传递参数:

prog -d -o ofile data0

// 此时argv数组的内容
argv[0] = "prog";
aegv[1] = "-d";
argv[2] = "-o";
argv[3] = "ofile";
argv[4] = "data0";
argv[5] = 0;

  • 怎样简单的返回复杂类型对象?
    有时候函数返回的类型比较复杂时代码会很晦涩, 比如返回数组指针:
int (*func(int i))[10];
// 可以这样理解上述代码
func(int i); // func的参数是int型
(*func(int i)); // func返回一个指针
(*func(inti))[10]; // 指针指向大小为10的数组
int (*func(int i))[10]; // 数组的元素为int型

我们可以采用下面三种方式简化这种操作:
1.使用类型别名

typedef int arrT[10]; // arrT是类型别名,表示含有10个整数的数组
using arrT = int[10]; // 同上一句
arrT* func(int i);    // func返回指向含有10个整数的数组的指针

2.使用尾置返回类型

// int(*)[10]是func的返回值
auto func(int i) -> int (*)[10]

3.使用decltype
当知道函数返回哪个数组时可以有decltype关键字声明返回类型:

int odd[] = {1, 3, 5, 7, 9};
int even[] = {0, 2, 4, 6, 8};
// 返回一个指向含有5int元素的数组的指针
decltype(odd) *arrPtr(int i)
{
    return (i % 2) ? &odd : &even;
}

  • 编译器是怎样确定调用哪个重载函数的?
    编译器确定调用哪个重载函数的过程称为函数匹配. 函数匹配分为三步:
    1. 确定本次调用的重载函数集(候选函数).
    2. 根据实参的数量在候选函数中选出可以执行(可能不止一个)本次调用的函数(可行函数).
    3. 根据实参的类型从可行函数中选择本次调用最终执行的函数(最匹配函数).

      这里”匹配”的含义很复杂, 我们将实参类型与形参类型完全相同称为精确匹配, 其他还有实参需要类型转换的匹配. 所谓最佳匹配指的是实参与形参的类型最接近.
      可以看出匹配的概念是很模糊的, 存在很多二义性, 如果匹配也像运算符一样有”优先级”就可以很好的解决这个问题.


  • 匹配的优先级
    所谓匹配的优先级指的就是优先级越靠前编译器越会判定其为最匹配.
    • 精确匹配, 包括以下情况:
      1. 实参与形参类型相同.
      2. 实参从数组类型或函数类型转换成对应指针类型.
      3. 向实参添加顶层const或者从实参中删除底层const.(顶层/底层const仅存在与引用与指针,注意下面的例子)
    • 通过const转换实现的匹配.
    • 通过类型提升实现的匹配.
    • 通过算术类型转换实现的匹配.
    • 通过类类型转换实现的匹配.
// 简要介绍几种情况

// 向实参添加顶层const
void func(const int &num);
int i = 8;
int &j = i;
func(j);

// 通过const转换
void func(const num);
int i = 8;
func(i);

// 通过类型提升
void func(int num);
char i = 8;
func(i);

// 通过算术类型转换
void func(int num);
float i = 8.2;
func(i);

// 一种重要的情况(注意与向实参添加顶层const的区别)
void lookip(int &);
void lookup(const int &);
const int i;
int j; // j不是引用, 不存在添加顶层const, 只有转换成const类型

lookup(i); // 只能调用void lookup(const int &)
looup(j); // 调用void lookup(int &)

  • 函数重载中为什么需要const_cast?
    看这个程序:
// const版本
const string &shorterString(const string &s1, const string &s2)
{
    return s1.size() <= s2.size() ? s1 : s2;
}

这个函数的参数和返回值都是const string &类型, 当我们用两个const string &实参调用函数时得到一个const string &类型, 这很好. 但是但我们用两个普通的string &调用函数时函数仍然可以工作, 只不过得到的仍然是const string &, 这个就不好了, 因为我们想要得到的是普通的string &. 这时const_cast可以完成这个任务.

// non-const版本
string &shorterString(string &s1, string &s2)
{
    auto &r = shorterString(const_cast<const string &> (s1), const_cast<const string &>(s2));
    return const_cast<string &>(r);
}

当我们用普通string &调用函数时调用non-const版本, 在这个版本中实参被转换成const string &型, 然后调用const版本, 返回const string &类型, 赋给r, 再用const_cast将const string &转换为普通string &.


  • 能不能理解这条语句: int ( * func(int))(int * , int) ?
    可以这样理解:
    1. func(int): func是一个带有一个int形参的函数.
    2. ( * func(int)): func返回一个指针.
    3. ( * func(int))(int * , int): 指针指向的内容是一个函数, 且取名为M, M函数带有一个int *型和一个int型形参.
    4. int ( * func(int))(int * , int): M函数的返回一个int类型.
      这里就出现了函数指针(指向函数的指针)这种类型的指针.
void (*func)(int *, int); // 这是一个函数指针
int array[10]; // 这是一个数组

现在再看看函数的声明大概可以猜到, 函数名和数组名一样, 代表了函数的入口地址.


基于开源大模型的教学实训智能体软件,帮助教师生成课前备课设计、课后检测问答,提升效率与效果,提供学生全时在线练习与指导,实现教学相长。 智能教学辅助系统 这是一个智能教学辅助系统的前端项目,基于 Vue3+TypeScript 开发,使用 Ant Design Vue 作为 UI 组件库。 功能模块 用户模块 登录/注册功能,支持学生和教师角色 毛玻璃效果的登录界面 教师模块 备课与设计:根据课程大纲自动设计教学内容 考核内容生成:自动生成多样化考核题目及参考答案 学情数据分析:自动化检测学生答案,提供数据分析 学生模块 在线学习助手:结合教学内容解答问题 实时练习评测助手:生成随练题目并纠错 管理模块 用户管理:管理员/教师/学生等用户基本管理 课件资源管理:按学科列表管理教师备课资源 大屏概览:使用统计、效率指数、学习效果等 技术栈 Vue3 TypeScript Pinia 状态管理 Ant Design Vue 组件库 Axios 请求库 ByteMD 编辑器 ECharts 图表库 Monaco 编辑器 双主题支持(专业科技风/暗黑风) 开发指南 # 安装依赖 npm install # 启动开发服务器 npm run dev # 构建生产版本 npm run build 简介 本项目旨在开发一个基于开源大模型的教学实训智能体软件,帮助教师生成课前备课设计、课后检测问答,提升效率与效果,提供学生全时在线练习与指导,实现教学相长。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值