题解 | 引用;引用和指针实现的区别;编写函数实现两数交换(引用方式)

编写函数实现两数交换(引用方式)

https://www.nowcoder.com/practice/19ff3da82df740a9a662aaf59aa41c5d

#include <iostream>
using namespace std;

// write your code here......
void myswap(int& x, int& y){
    int temp = x;
    x = y;
    y = temp;         
}

int main() {

    int m, n;
    cin >> m;
    cin >> n;

    // write your code here......
    myswap(m, n);

    cout << m << " " << n << endl;

    return 0;
}

一、先解答简单问题:int& aint &a 有区别吗?

结论:无任何本质区别!仅书写习惯不同

C++ 编译器对「空格」不敏感,这两种写法的语法含义、编译结果完全一致,都是定义一个 int 类型的引用变量a

核心原因:

& 在这里是「引用语法标记」,不是运算符,编译器解析时会忽略空格,把 int&/int & 整体识别为「int类型的引用类型」。

示例验证(三种写法都合法,效果完全一样):

#include <iostream>
using namespace std;

int main() {
    int num = 10;
    
    // 写法1:int& a(推荐,直观体现“int&是引用类型”)
    int& a = num;
    // 写法2:int &a(常见,把&和变量绑定)
    int &b = num;
    // 写法3:甚至加多个空格也可以
    int  &  c = num;
    
    a = 20;
    cout << num << " " << b << " " << c << endl; // 输出 20 20 20
    return 0;
}

⚠️ 唯一需要注意的「坑点」(多变量定义时):

如果一行定义多个变量,& 只绑定紧邻的第一个变量,此时空格会影响语义(但不是int& aint &a的区别,是多变量定义的语法规则):

int num = 10;
int& a, b; // a是int引用(绑定num前要初始化),b是普通int变量!
int &c = num, d; // c是int引用(绑定num),d是普通int变量!

✅ 建议:定义多个引用时,每行只定义一个,避免歧义:

int& a = num;
int& b = num; // 清晰无坑

二、核心问题:引用 vs 指针 实现swap函数的区别(全维度对比)

先给出两种实现的完整代码,再从「语法、原理、安全性、使用方式」等维度拆解区别,对比更直观。

第一步:先看两种swap的实现代码

1. 引用版swap(你之前学的)

void swapByRef(int& x, int& y) { // 形参是引用(别名)
    int temp = x; // 直接用x/y,等价于用实参本身
    x = y;
    y = temp;
}

// 调用方式
int main() {
    int m=10, n=20;
    swapByRef(m, n); // 直接传变量,无需取地址
    cout << m << " " << n << endl; // 20 10
    return 0;
}

2. 指针版swap

void swapByPtr(int* x, int* y) { // 形参是指针(存储实参的地址)
    int temp = *x; // 必须解引用*,才能访问指针指向的变量
    *x = *y;
    *y = temp;
}

// 调用方式
int main() {
    int m=10, n=20;
    swapByPtr(&m, &n); // 必须传变量的地址(&m/&n)
    cout << m << " " << n << endl; // 20 10
    return 0;
}

第二步:核心区别(8个维度对比,新手必记)

1. 形参类型

int& x

(引用,变量的别名)

int* x

(指针,存储变量的地址)

2. 调用方式

直接传变量:

swapByRef(m, n)

必须传地址:

swapByPtr(&m, &n)

3. 函数内操作方式

直接用x/y(等价于操作实参)

必须解引用

*x/*y

(通过地址访问变量)

4. 底层原理

引用是「指针的语法糖」(编译器自动处理地址),汇编层面和指针等价

显式操作地址,程序员手动控制解引用

5. 可重定向性

引用一旦绑定实参,

不能切换指向

(比如x无法再绑定其他变量)

指针可以随时指向其他变量(比如

x = &k

6. 空值/野引用问题

引用

不能为空

(定义时必须绑定变量),无“野引用”

指针可空(

int* x = nullptr

)、可成野指针(指向无效地址),风险高

7. 安全性

极高(编译器强制初始化,无空引用)

较低(易犯空指针/野指针/解引用错误)

8. 语义表达

强调“变量的别名”,语义更直观(swap是交换两个变量)

强调“地址操作”,语义偏底层

第三步:关键细节拆解(新手易混淆的点)

1. 最直观的区别:调用和操作方式

  • 引用版:调用时不用写&,函数内不用写*,代码更简洁;
  • 指针版:调用时必须写&(取地址),函数内必须写*(解引用),代码更“底层”。

2. 安全性差异(核心优势:引用更安全)

  • 引用的“强制初始化”规则:❌ 错误:void swap(int& x) { ... } 如果调用时传常量(swap(10)),编译器直接报错;❌ 错误:定义引用时不初始化(int& x;),编译报错;→ 从语法层面杜绝了“空引用”“野引用”的可能。
  • 指针的风险点:❌ 空指针解引用:int* x = nullptr; *x = 10;(编译通过,运行崩溃);❌ 野指针:int* x = (int*)0x123456; *x = 10;(指向无效地址,未定义行为);❌ 忘记解引用:void swap(int* x, int* y) { int temp = x; x = y; y = temp; }(交换的是指针地址,不是变量值,逻辑错误)。

3. 底层等价,但语法层面不同

从汇编代码角度,引用和指针实现swap的指令完全一致(都是操作内存地址),但C++语法层面:

  • 引用是“编译器帮你管地址”,不用手动处理;
  • 指针是“程序员自己管地址”,灵活但易出错。

4. 可重定向性的影响(swap场景用不到,但面试常问)

比如想让swap函数里的形参指向新变量:

// 指针版可以(但swap场景没必要)
void testPtr(int* x) {
    int k = 30;
    x = &k; // 指针x切换指向k,合法
}

// 引用版不行(编译不报错,但逻辑不是切换指向)
void testRef(int& x) {
    int k = 30;
    x = k; // 不是切换指向,是把k的值赋给x(即原变量)
}

三、总结(核心结论+使用建议)

1. 关于int& aint &a

  • 无本质区别,仅书写习惯;
  • 推荐写int& a(把int&看成整体的“引用类型”),更易读,减少多变量定义的歧义。

2. 关于引用/指针实现swap:

  • 功能上:两者都能实现交换,底层等价;
  • 易用性:引用版更简洁,代码量少,新手不易出错;
  • 安全性:引用版完胜(无空/野引用风险);
  • 灵活性:指针版更灵活(可切换指向),但swap场景用不到;
  • 面试/笔试考点: 面试官常问“引用和指针的区别”,swap是最经典的案例;答的时候要覆盖「初始化、可重定向、空值、语法、安全性」这几个核心点。

3. 实际开发建议:

  • 普通swap场景:优先用引用(简洁、安全);
  • 底层操作/需要动态切换指向:用指针(比如链表、动态内存分配);
  • C++11及以上:直接用标准库的std::swap(底层也是引用实现),不用自己写。

这两个知识点是C++基础中的基础,尤其是引用和指针的区别,是面试高频题,把swap这个案例吃透,就能举一反三 ✨

C/C++题解 文章被收录于专栏

记录个人编程题的解题思路以及学习的新知识

全部评论

相关推荐

各位前辈好,先说声抱歉,可能又是一篇“求骂醒”的帖子,但我真的需要一个方向。我的情况比大多数人都糟糕:双非软件工程,大四,马上毕业了,0实习经历,0工作经验。秋招根本没参加,原因很傻——我一头扎进了一个自己觉得“挺有意思”的项目里,天真的以为把项目做好工作自然会找上门。现在春招也快结束了,我才如梦初醒,发现简历投出去基本石沉大海。我没有什么能拿出手的背景,唯一能说的就是这个从后端到前端全栈独立开发的电影推荐平台。我知道在各位前辈眼里这大概率就是个小玩具,但我确实是下了功夫去琢磨的,它不是什么网上扒的代码,下面这些是我自己琢磨并落地的东西:项目概况:Spring&nbsp;Boot&nbsp;+&nbsp;MyBatis-Plus&nbsp;+&nbsp;Redis&nbsp;+&nbsp;JWT&nbsp;+&nbsp;MySQL&nbsp;+&nbsp;Vue3(前端是AI辅助生成的)我自己觉得花了心思的几个点:1.&nbsp;推荐算法落地:没有照搬别人的推荐逻辑。我是基于用户多维行为数据(评分、收藏、浏览时长)去计算标签权重,然后用“评分×log(热度+1)”的公式做加权排序;冷启动场景用热门数据兜底。推荐结果用Redis的ZSet缓存,用户行为一变化就主动删缓存触发重算。2.&nbsp;缓存体系设计:不是那种“面试八股文背完就扔”的表面理解。我实际遇到了缓存穿透和击穿的问题,然后自己用空值缓存+逻辑过期去解决。热门电影定时预热、批量查询用multiGet减少IO次数,还封装了MyCacheUtils通用模板,让整个项目其他模块也能复用这套缓存逻辑。3.&nbsp;并发与一致性:用Redis的SET&nbsp;NX&nbsp;EX实现了收藏/点赞的分布式锁,key精确到“用户+操作对象”级别,不是粗粒度的一锁全锁。异常回滚时Redis和MySQL数据一致性问题也思考并落地了。验证码的原子性校验用了Lua脚本来保证。4.&nbsp;性能是真实数据:我用JMeter做了2000并发的压测,引入Redis缓存体系后,推荐接口平均响应从6466ms降到155ms,吞吐量翻了一倍,缓存命中率干到98%以上。这些数据不是编的,是我自己反复调优跑出来的。说实话,做完这些的时候,看着压测报告我是挺兴奋的,觉得“这也算出活儿了吧”。但现实是,0实习好像成了我简历上的原罪,很多公司直接筛选条件就把我过滤了。所以我想跪求各位前辈指点我几个问题,每一条我都认真看、认真执行:1.&nbsp;关于简历:0实习的应届生,还有资格谈“项目亮点”吗?我这项目,是不是在专业面试官眼里就是一个“低配版培训项目”?如果这个项目还有救,该怎么在简历上呈现,才能让HR或者面试官至少愿意给我一个电话面试?如果没有,一个0实习的应届生到底该在简历上写什么?2.&nbsp;关于面试:如何用项目细节证明“我虽然没实习但真的能干活”?我挺怕面试官看到我没有实习经历就直接失去兴趣。真到了面试那一步,我该怎么引导对话,用上面这些技术细节去对抗“没实习=没工程经验”的刻板印象?比如缓存那块,怎么从“我解决了击穿”讲出一个有技术判断力和工程思维的完整故事?3.&nbsp;关于求职策略:错过了黄金窗口期,现在该冲什么样的公司?大厂我肯定不奢望了。现在这个时间点,我应该去投那些小公司和外包吗?要不要把薪资预期降到最低先入行再说?对于0实习的应届生,什么样的公司是真的有机会让我进去学技术、积累经验的?4.&nbsp;关于未来:如果现在直接找不到工作,我该怎么办?这段时间我想好了,如果实在是找不到研发岗,我要不要去干测试或者运维先入行?还是找家小公司被压榨一年攒个经验?还是干脆先找个其他工作边干边学等下一轮秋招?我什么建议都能接受。我知道自己起步晚了,代价得自己扛。现在唯一能做的就是面对现实,然后找到一条最有可能逆袭的路。希望前辈们能给我指个方向,即使简单几句“没救了”或者“还能救,去做XXX”我都非常感激。
jiestart:这简历肯定没面试的,你得包装个实习再加一个agent项目才有希望
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务