一文看懂:信息学奥赛中的指针和引用
2026/8/1 5:12:47 网站建设 项目流程

适用对象:有一定 C++ 基础,正在备赛 CSP-J/S 或 NOIP 的学生。

一句话先记住:

指针存地址,引用是别名。竞赛里引用天天用,指针偶尔见。

一、先搞懂内存地址

变量存在内存里,每个变量都有自己的“门牌号”——地址。

inta=10;cout<<&a;// 输出 a 的地址,比如 0x7ffee3f9a8dc

&a就是取 a 的地址。

二、指针(Pointer)

指针就是一个存地址的变量

inta=10;int*p=&a;// p 存着 a 的地址

核心操作

代码含义
int *p定义一个指针变量
p = &a让 p 指向 a
*p取 p 指向的那个变量(解引用)
*p = 20通过指针修改 a 的值
inta=10;int*p=&a;*p=20;// a 变成 20intb=30;p=&b;// p 改为指向 b(指针可以换指向)*p=40;// b 变成 40

指针能换指向,这是它和引用最大的不同。

三、引用(Reference)

引用就是给变量起个外号、别名

inta=10;int&r=a;// r 是 a 的别名

核心操作

代码含义
int &r = ar 是 a 的引用,必须初始化
r = 20就是a = 20
&r取 a 的地址(r 没有自己的地址)
inta=10;int&r=a;r=20;// a 变成 20intb=30;// 引用不能换绑!r = b 只是把 b 的值赋给 a

四、指针 vs 引用:核心区别

对比维度指针引用
本质存地址的变量变量的别名
必须初始化?否,可以先定义后赋值,必须绑定到一个变量
能否为空?可以(nullptr不能
能否改变指向?,可以指向别的变量不能,一生绑定一个
如何访问值?需要*解引用直接使用,像普通变量
内存占用?占用内存(存地址)不占额外内存(本质是别名)
安全性容易出错(空指针、野指针)安全,不容易出错
竞赛中用哪个?用于底层实现用于日常编码(传参、遍历)

五、->运算符是什么?

->是竞赛中天天要用的运算符,它的本质是:

p->member 等价于(*p).member

就是“取出指针指向的那个东西,然后访问它的成员”。

.->的区别

左边是什么用什么示例
普通对象.Node u; u.val = 10;
指针->Node *p; p->val = 10;
引用.Node &r = u; r.val = 10;
structNode{intval;Node*left,*right;};Node u;Node*p=&u;Node&r=u;u.val=10;// 对象 → 用 .p->val=20;// 指针 → 用 ->r.val=30;// 引用 → 用 .(跟对象一样)

六、竞赛实战场景

场景一:函数传参(避免拷贝,最常用)

voiddfs(Node*u){if(u==nullptr)return;cout<<u->val;dfs(u->left);dfs(u->right);}

场景二:范围遍历修改

for(int&x:vec){x*=2;// 直接改原数组,不拷贝}

场景三:手写二叉树/链表

structNode{intval;Node*next;// 指向下一个节点};voidprint(Node*head){for(Node*p=head;p!=nullptr;p=p->next){cout<<p->val<<" ";}}

场景四:链式前向星(数组模拟指针)

structEdge{intto,w,next;// next 就是“指针”,存的是下一条边的下标}edges[N];inthead[N];voidadd(intu,intv,intw){edges[++cnt]={v,w,head[u]};head[u]=cnt;// 把新的边的下标存进去}// 遍历:用 . 访问(因为下标不是指针)for(inti=head[u];i!=-1;i=edges[i].next){// 处理 edges[i]}

场景五:Node *&—— 指针的引用

voidinsert(Node*&u,intx){if(u==nullptr){u=newNode{x,nullptr,nullptr};return;}if(x<u->val)insert(u->left,x);elseinsert(u->right,x);}Node*root=nullptr;insert(root,10);// root 本身被修改了(从 nullptr 变成指向新节点)

为什么用Node *&而不是Node *

// 如果写成 Node *u(传指针的值)voidinsert(Node*u,intx){u=newNode{x,nullptr,nullptr};// 修改的是指针的副本}// 外面的 root 还是 nullptr,改不了!// 要用引用传递指针本身voidinsert(Node*&u,intx){// u 是 root 的别名u=newNode{x,nullptr,nullptr};// 直接修改 root}

一句话:想修改指针本身指向什么,就得传指针的引用。

七、常见陷阱

错误正确做法
int *p; *p = 10;没初始化就用定义时赋值int *p = &a;
Node *p = nullptr; cout << p->val;空指针解引用用前检查if (p != nullptr)
Node *p; cout << p->val;野指针初始化Node *p = new Node;
int &r;引用没初始化必须初始化int &r = a;

八、记牢这张图

指针和引用 │ ┌───────────────┴───────────────┐ │ │ 指针 引用 │ │ 存地址的变量 变量的别名 可以重新指向 不能换绑 可以为空 必须初始化 用 * 解引用 直接用 用 -> 访问成员 用 . 访问成员

九、对竞赛来说够用的标准

必须掌握理解即可暂时不用管
引用作函数参数指针的基本概念二级指针int **p
范围遍历用&修改指针和引用的区别函数指针
理解*&含义nullptrnew/delete
->访问结构体成员Node *&的用法复杂指针运算

十、一句话总结

指针是“存地址的变量”,引用是“变量的别名”。

引用安全、直观、高效,竞赛里天天用。指针灵活但容易犯错,主要用于底层实现和高级数据结构。掌握引用的用法,理解指针的概念,够了。

十一、检查你是否掌握了

inta=5,b=10;int*p=&a;int&r=b;*p=20;// a = ?r=30;// b = ?cout<<a<<" "<<b<<endl;// 输出什么?

答案:a = 20b = 30

附录:你可能还疑惑的

Q1:为什么引用的本质是“别名”?

因为引用不占额外内存,它和原变量共用同一块内存地址。对引用做任何操作,都直接反映在原变量上。

Q2:int *&int &*哪个对?

int *&是对的(指针的引用)。int &*是错的(引用的指针,不存在)。

Q3:竞赛里指针和引用哪个重要?

引用重要得多。函数传参、STL 容器操作都离不开引用。指针主要用于手写链表、二叉树、平衡树等数据结构的底层实现。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询