写代码的时候,你有没有遇到过这样的情况:在函数里改了一个变量,结果外面的值也被动了?或者相反,明明改了却没变?这背后很可能就是传值和传引用在“作怪”。
传值:各过各的,互不干扰
传值就像复印一份文件。你在函数里拿到的是原数据的一份副本,怎么折腾都不会影响原件。比如在 C 语言里,基本类型默认就是传值:
void changeValue(int x) {
x = 100;
printf("函数内 x = %d\n", x);
}
int main() {
int a = 10;
changeValue(a);
printf("函数外 a = %d\n", a);
return 0;
}
输出结果是:
函数内 x = 100
函数外 a = 10
看,函数里的修改对 a 没有任何影响。这就是传值的典型表现——安全但可能浪费内存,尤其是复制大对象时。
传引用:牵一发而动全身
传引用更像是共享同一个文档链接。你拿到的不是副本,而是原始数据的位置。改了它,所有用这个引用的地方都会看到变化。C++ 支持直接传引用:
void changeRef(int& x) {
x = 200;
}
int main() {
int b = 20;
changeRef(b);
printf("函数外 b = %d\n", b); // 输出 200
return 0;
}
这里 b 的值真的被改成了 200。这种机制效率高,避免了复制开销,但也容易“误伤”原数据,调试起来有时挺头疼。
Python 是个“混搭”高手
Python 不像 C++ 那样明确区分 & 符号,但它其实也有类似行为,只是规则藏得深。它按“对象可变性”来决定:
不可变对象(比如整数、字符串)看起来像传值:
def modify_num(x):
x = 50
num = 10
modify_num(num)
print(num) # 还是 10
可变对象(比如列表),就表现出传引用的效果:
def modify_list(lst):
lst.append(4)
my_list = [1, 2, 3]
modify_list(my_list)
print(my_list) # 变成 [1, 2, 3, 4]
很多人初学 Python 时在这里踩坑。你以为传的是值,结果原列表被改了。想避免?可以手动传副本:modify_list(my_list.copy())。
JavaScript 的“中间路线”
JS 所有参数都是传值,但对象类型传的是“引用的值”,听起来绕,其实就像把门牌号复印了一份给人。你拿门牌号能进去改屋子里的东西,但换门牌不影响原来的那份。
function changeObj(obj) {
obj.name = '新名字';
}
let user = {name: '老王'};
changeObj(user);
console.log(user.name); // 新名字
但如果在函数里重新赋值 obj,那就断链了:
function reassignObj(obj) {
obj = {name: '完全新建'};
}
reassignObj(user);
console.log(user.name); // 还是 '新名字'
因为这只是改了“本地门牌号”,不影响原来的 user 指向。
实际开发中的小建议
如果你写的函数只是读数据,随便传。但要是会修改,最好在文档里写清楚,或者干脆设计成返回新值,而不是改动输入。这样别人调用时心里更有底,也不容易出 bug。
理解传值和传引用,不是为了背概念,而是让你更清楚自己的代码到底在动谁的数据。下次改完函数发现外面的变量“莫名其妙”变了,别急着骂环境,先看看是不是参数传递方式惹的祸。