玄武纪8 月 · CSP-J 初赛打卡DAY 19 / 20

Day 19 引用、函数参数与作用域

建议用时:20~25 分钟
今日目标:理解引用是已有变量的别名,分清值传递、引用传递和指针传递,能够追踪函数调用前后实参的变化。
大纲定位:入门级引用、函数参数与变量作用域;不展开右值引用、模板引用折叠等高级内容。

一、引用是已有变量的另一个名字

int a = 10;
int &r = a;

ra 的引用,也就是 a 的别名。此后读 r 就是在读 a,给 r 赋值就是给 a 赋值。

r = 20;
cout << a;   // 20

引用必须在定义时绑定一个有效对象:

int &r;      // 错误:没有初始化
int &r = a;  // 正确

引用一旦绑定,不能再改为另一个变量的别名。假设 int &r=a;,再执行 r=b;,含义是把 b 的值赋给 a,不是让 r 改绑到 b

值传递、引用传递和指针传递知识卡

二、值传递:函数得到一份副本

void addOne(int x) {
    x++;
}

int a = 5;
addOne(a);

形参 x 得到 a 当前值的一份副本。函数中把 x 改成 6,主函数里的 a 仍是 5。

值传递的草稿纸画法是画两个盒子:a=5,调用后另建 x=5。函数结束时,局部形参 x 失效。

三、引用传递:形参直接成为实参的别名

void addOne(int &x) {
    x++;
}

int a = 5;
addOne(a);

形参写成 int &x 后,调用期间 x 是实参 a 的别名,x++ 会把 a 改成 6。函数调用处仍写 addOne(a),不需要再写 &

引用参数常用于:

若只想避免复制而不允许函数修改实参,可以使用常量引用:

void print(const string &s) {
    cout << s;
}

函数能读取 s,但不能通过 s 修改原字符串。

四、指针传递:传入地址,再解引用修改

void addOne(int *p) {
    (*p)++;
}

int a = 5;
addOne(&a);

指针形参接收地址,调用处要传 &a,函数内用 *p 访问 a。引用与指针都能让函数修改外部变量,但写法不同:

方式 形参 调用 函数内修改
值传递 int x f(a) x=...,不影响 a
引用传递 int &x f(a) x=...,会影响 a
指针传递 int *p f(&a) *p=...,会影响 a

引用是变量别名知识卡

五、交换函数为什么常用引用

错误版本:

void mySwap(int a, int b) {
    int t = a;
    a = b;
    b = t;
}

这里交换的只是两份副本,调用者的变量不变。正确的引用版本:

void mySwap(int &a, int &b) {
    int t = a;
    a = b;
    b = t;
}

若写成指针版本,则函数体使用 *a*b,调用时传入两个变量的地址。初赛会把三种写法放在一起,关键不是背函数名,而是看形参声明。

六、作用域决定“这个名字在什么地方有效”

在函数或语句块内部定义的变量是局部变量,只在相应作用域内可见:

int x = 3;
if (x > 0) {
    int y = 5;
    cout << y;  // 正确
}
// cout << y;  // 错误:y 已离开作用域

内层可以出现与外层同名的局部变量,此时内层名字会暂时遮蔽外层名字:

int x = 1;
{
    int x = 2;
    cout << x;  // 2
}
cout << x;      // 1

引用和指针还要考虑目标的生命周期:不能继续使用已经离开作用域的局部变量地址或引用。初赛若出现“函数返回局部变量的引用/地址”,应高度警惕悬空问题。

作用域与变量生命周期知识卡

七、函数程序阅读的固定表格

遇到一次函数调用,先写一行“形参—实参关系”:

形参 a:值传递,单独副本
形参 b:引用传递,别名 → 实参 y
形参 c:指针传递,保存地址 → 实参 z

然后逐句修改:

今日选择题

第 1 题为 2025 年 CSP-J 第一轮单选原题;第 2~5 题为专题练习;第 6~8 题围绕同一段函数程序阅读。

  1. 【CSP-J 2025·第 10 题】 考虑以下 C++ 函数,在 main 函数调用 solve 后,xy 的值分别是( )。

    void solve(int &a, int b) {
        a = a + b;
        b = a - b;
        a = a - b;
    }
    
    int main() {
        int x = 5, y = 10;
        solve(x, y);
    }
    

    A. 5, 10  B. 10, 5  C. 10, 10  D. 5, 5

  2. 执行下面的代码后,x 的值是( )。

    void f(int a) { a += 3; }
    int x = 7;
    f(x);
    

    A. 7  B. 10  C. 3  D. 无法确定

  3. 执行下面的代码后,x 的值是( )。

    int x = 7;
    int y = 12;
    int &r = x;
    r = y;
    

    A. 7  B. 12  C. y 的地址  D. r 改为 y 的别名,x 不变

  4. 下列引用定义正确的是( )。

    A. int &r;
    B. int x=3; int &r=x;
    C. int &r=nullptr;
    D. int x=3; int &r=&x;

  5. 已知 int x=3; const int &r=x;,下列说法正确的是( )。

    A. 可以执行 r=5 修改 x
    B. r 可以随时改绑到另一个变量
    C. 不能通过 r 修改所绑定的值
    D. r 没有绑定任何对象

主题程序阅读

阅读下面的程序:

#include <iostream>
using namespace std;

void adjust(int a, int &b, int *c) {
    a += 1;
    b += a;
    *c = b - a;
}

int main() {
    int x = 3, y = 5, z = 8;
    adjust(x, y, &z);
    cout << x << ' ' << y << ' ' << z << endl;
    return 0;
}
  1. adjust 中执行 a += 1; 后,形参 a 和主函数变量 x 的值分别是( )。

    A. 4, 4  B. 4, 3  C. 3, 4  D. 3, 3

  2. 执行 b += a; 后,主函数变量 y 的值是( )。

    A. 5  B. 8  C. 9  D. 13

  3. 程序最终输出( )。

    A. 4 9 5  B. 3 9 5  C. 3 5 8  D. 4 5 4

暂停 · 先完成并提交

先标形参类型,再追数值

请先独立完成全部题目,并到玄武 OJ 提交今日答案。

  • 值参数画副本,引用参数画别名,指针参数画箭头;
  • 函数结束后只保留外部变量的变化;
  • 提交后再向下查看完整题目和解析。
继续向下:每道题的完整题目、答案与解析
本页用于 CSP-J 第一轮自主复习。请先看知识卡并独立完成练习,提交玄武 OJ 后再查看解析。
← 上一天 返回学习中心 下一天 →