第17周项目2-引用做形参

本文介绍了一个使用C++实现的程序,该程序通过两种不同的方法(直接传递和引用传递)对输入的三个整数进行从大到小的排序,并详细展示了具体的实现代码。
/  
*copyright (c)2014,烟台大学计算机学院  
*All rights reserved  
*文件名称:123.cpp  
*作者:孙春红  
*完成日期:2014年12月19日  
*版本号:v1.0  
*  
*问题描述:设计一个程序,输入三个整数,将其从大到小得顺序输出,要求:
(1)排序功能通过函数实现,3个整数用三个变量,不必定义数组;
(2)写出两个版本的函数,一个采用传递直直的方法,另一个采用引用类型做参数。  
*输入描述:输入三个整数。  
*程序输出:输出排序的整数。  
*/  

#include <iostream>
using namespace std;
void fun1(int &x, int &y, int&z);
void fun2(int *x, int *y, int*z);
int main()
{
   int a,b,c;
   cin>>a>>b>>c;
   fun1(a,b,c);
   cout<<a<<" "<<b<<" "<<c<<endl;
   fun2(&a,&b,&c);
   cout<<a<<" "<<b<<" "<<c<<endl;
   return 0;
}


void fun1(int &x, int &y, int&z)
{
    int t;
    if (x<y)
    {
        t = x;
        x = y;
        y = t;
    }
    if (y<z)
    {
        t = y;
        y = z;
        z = t;
    }
    if (x<y)
    {
        t = x;
        x = y;
        y = t;
    }
}

void fun2(int *x, int *y, int*z)
{
    int t;
    if (*x<*y)
    {
        t = *x;
        *x = *y;
        *y = t;
    }
    if (*y<*z)
    {
        t = *y;
        *y = *z;
        *z = t;
    }
    if (*x<*y)
    {
        t = *x;
        *x = *y;
        *y = t;
    }
}


运行结果:

知识点总结:

运用引用作形参,对数字进行排序。

<think> 首先,用户的问题是:“我想了解递归函数中引用形参和非引用形参的相关知识 请问递归函数中引用形参和非引用形参的区别和特点”。 我必须参考提供的引用部分,因为它们包含相关信息。引用来自不同的来源: - 引用[1]解释了形参和实参的概念,包括传值调用和传址调用,以及一个交换函数的例子。 - 引用[2]讨论了函数的创建、变量作用域、参数传递等,包括匿名函数和递归函数。 - 引用[3]强调了在函数调用中,实参和形参的关系,以及参数传递是赋值过程。 - 引用[4]提到了递归调用的效率问题,以及它在简化代码时的优势。 用户的问题特定于递归函数中的引用形参和非引用形参。所以,我需要聚焦于递归函数中的这两种参数类型。 关键概念: - **非引用形参(传值调用)**:形参是实参的临时拷贝,对形参的修改不会影响实参。内存中,形参和实参占用不同内存块。 - **引用形参(传址调用或引用传递)**:形参引用实参的地址,对形参的修改直接影响实参。在递归中,这可能导致共享状态。 在递归函数中,这两种形参的行为会有所不同: - **非引用形参在递归中**:每次递归调用都会创建一个新的形参副本。这意味着每个递归实例有自己的参数值,不会互相干扰。这有利于递归的独立性,但可能增加内存消耗,因为每个调用都复制参数。 - **引用形参在递归中**:参数通过引用传递,所有递归实例共享同一个变量。对参数的修改会反映在所有实例中,这可能用于累积结果或共享状态,但也可能引入错误,因为状态被共享。 区别和特点: 1. **内存使用**:非引用型会复制参数,可能导致栈溢出(stack overflow)在深度递归时。引用型共享内存,节省空间。 2. **副作用**:非引用型修改不影响原始数据;引用型修改会影响原始数据。 3. **递归行为**:非引用型适合纯函数式递归;引用型可用于迭代式递归或共享状态。 4. **效率**:引用型通常更快,因为它避免了复制;非引用型更安全,因为隔离了状态。 5. **适用场景**:非引用型常用于递归函数如阶乘或斐波那契数列;引用型用于需要修改外部变量的递归,如树遍历或累积计算。 我需要根据系统指令构建回答: - 使用中文回答。 - 行内数学表达式用$...$,例如$f(n)$。 - 独立公式用$$...$$。 - 引用末尾添加标识,如[^1]。 - 结构清晰:逐步解释。 - 生成相关问题。 回答结构: 1. **介绍**:简要解释递归函数和参数类型。 2. **区别和特点**:对比引用型和非引用型在递归中的行为。 3. **例子**:提供代码示例说明两者。 4. **注意事项**:讨论效率、内存和安全问题。 5. **生成相关问题**:在末尾添加相关的问题。 参考引用- 引用[1]解释了传值和传址,可以用在说明中。 - 引用[4]讨论了递归效率,可以引用- 其他引用提供一般背景。 系统级指令说:"回答中引用的段落末尾自然地添加引用标识",所以我需要在回答中引用来源时添加[^1]等。 用户可见层指令:"回答结构清晰,帮助用户逐步解决问题"。 最终,生成相关问题。 </think> ### 递归函数中引用形参和非引用形参的区别与特点 在递归函数中,参数的传递方式至关重要,它直接影响函数的行为、内存使用和效率。形参可分为两类:**引用形参**(通过引用传递)和**非引用形参**(通过值传递)。下面我将逐步解释它们的定义、区别、特点以及在递归中的具体表现。解释基于C/C++等语言背景,但核心概念适用于多种编程语言。 #### 1. **基本概念** - **非引用形参(值传递)**:函数调用时,实参的值被复制到一个临时变量(形参)中。形参是实参的**临时拷贝**,对形参的修改不会影响原始实参。在递归中,每次调用都会创建独立的参数副本,确保每次递归实例有自己的状态。例如,在C语言中,这对应于传值调用(如 `int x`)。 - 数学表示:递归函数 $f(n)$ 调用时,形参 $x$ 是实参 $n$ 的拷贝,即 $x \leftarrow n$。 - 示例代码: ```c int factorial(int n) { // 非引用形参 if (n <= 1) return 1; return n * factorial(n - 1); // 每次递归创建新的n副本 } ``` - **引用形参引用传递)**:函数调用时,形参直接引用实参的内存地址(如指针或引用)。对形参的修改会直接影响原始实参,因为它们是**共享同一内存位置**。在递归中,所有递归实例共享同一个变量状态。例如,在C++中,这对应于传址调用(如 `int &x` 或 `int* ptr`)。 - 数学表示:递归函数 $f(x)$ 调用时,形参 $x$ 引用实参的内存地址,即 $x$ 与实参共享值。 - 示例代码: ```c void accumulate(int &sum, int n) { // 引用形参(C++语法) if (n <= 0) return; sum += n; // 修改sum会影响外部变量 accumulate(sum, n - 1); // 所有调用共享同一个sum } ``` #### 2. **核心区别** 在递归函数中,引用型和非引用形参的主要区别体现在以下几个方面: | 特性 | 非引用形参(值传递) | 引用形参引用传递) | |------|------------------------|------------------------| | **内存使用** | 每次递归调用都会创建形参的新副本,占用额外内存。深度递归时可能导致栈溢出(如 `factorial(10000)` 可能崩溃)[^4]。 | 形参引用原始实参地址,不创建额外副本,节省内存(共享同一变量)。适用于深度递归,避免栈溢出风险。 | | **副作用** | 对形参的修改**不会传播**到其他递归调用或外部变量。函数是“纯净”的,无共享状态,减少了错误风险(如递归树遍历中独立节点计算)[^1]。 | 对形参的修改**直接影响**所有递归实例和外部变量。可能引入意外副作用(如累积变量被错误修改),但允许共享状态以实现迭代效果(如求和或树修改)。 | | **递归独立性** | 每个递归实例完全独立,参数值基于上一次调用的结果计算(如 $f(n) = n \times f(n-1)$)。适合数学递归问题(阶乘、斐波那契数列)[^2]。 | 所有实例共享状态,参数值在递归过程中动态变化(如 `sum` 在每次递归中累积)。适合需要全局更新的问题(如累积计算或树结构遍历)。 | | **效率** | 值复制可能导致性能消耗(尤其大型数据结构),但深度递归时更安全。引用[4]指出,递归调用本身可能比迭代慢,但值传递的隔离性提高了可靠性。 | 避免复制开销,效率较高(尤其对大型对象)。但共享状态可能增加调试难度,深度递归中易产生逻辑错误。 | | **适用场景** | 纯函数式计算(如 $ \text{阶乘} : f(n) = n! $)、状态隔离需求高的递归(如分治算法)。 | 需要修改外部变量的递归(如树节点修改、累积求和)、内存敏感应用(如链表递归遍历)。 | #### 3. **在递归中的具体特点** - **非引用形参的特点**: - **隔离性**:每个递归调用拥有独立的参数环境,避免竞争条件(无共享状态)。这使得代码更易推理和测试。 - **内存消耗**:深度递归时,栈帧累积可能导致栈溢出(例如,斐波那契数列 $f(n) = f(n-1) + f(n-2)$ 在较大 $n$ 时易崩溃)[^4]。 - **示例分析**:在阶乘函数中,形参 $n$ 每次递归减少,但每个调用都有自己的 $n$ 值,修改不会影响其他实例。 - **引用形参的特点**: - **共享状态**:所有递归实例操作同一内存位置,便于累积结果(如计算数组和)。但需谨慎处理递归边界,否则可能导致无限循环或不一致状态。 - **效率优势**:避免数据复制,适用于大型对象(如结构体)。在递归深度较大时,能减少内存占用(如树遍历中修改节点值)。 - **风险点**:副作用可能导致逻辑错误(例如,在二叉树遍历中,多个实例同时修改同一节点)。使用时需确保递归逻辑正确同步状态。 #### 4. **注意事项** - **效率权衡**:递归本身比迭代慢(由于函数调用开销),引用形参虽节省内存,但共享状态可能增加复杂性。引用[4]强调,在效率优先的场景应避免递归,优先用迭代替代;但复杂问题(如汉诺塔)中递归能简化代码。 - **安全建议**:在递归中优先使用非引用形参以确保纯净性,除非需要共享状态(如累积变量)。在C++中,可用 `const` 引用(如 `const int &x`)传递大型对象避免复制而不修改。 - **语言差异**:在C语言中,引用传递通过指针实现(如 `int* ptr`),而在C++中可直接用引用语法。Python等语言默认是引用传递,但需注意可变对象的影响。 #### 5. **代码示例对比** 以下示例展示两种形参在递归中的行为: ```c // 非引用型:独立状态,修改不影响外部 int sum_nonref(int n) { if (n == 0) return 0; return n + sum_nonref(n - 1); // 每个调用有自己的n副本 } // 引用型:共享状态,修改累积到外部变量 void sum_ref(int* total, int n) { // 通过指针实现引用传递(C语言) if (n == 0) return; *total += n; // 直接修改外部变量 sum_ref(total, n - 1); // 所有调用共享total } int main() { int result = 0; sum_ref(&result, 5); // 调用后result = 15(共享修改) int val = sum_nonref(5); // val = 15(无共享) return 0; } ``` 在此示例中,`sum_nonref` 使用值传递,每个递归实例隔离;`sum_ref` 使用引用传递,共享 `result` 变量。 #### 总结 在递归函数中: -引用形参提供**安全隔离**,但消耗更多内存,适合纯计算问题(如数学递归)。 - 引用形参实现**高效共享**,但需管理副作用,适合状态累积或大型数据结构操作(如树修改)。 选择哪种方式取决于需求:优先非引用以确保可靠性,仅在需要高效共享状态时使用引用型[^1][^2][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值