引入:
一般情况下,形参的改变不会影响实参
值传递
什么叫“一般情况下,形参的改变不会影响实参”呢???
要清楚地知道上面的是什么意思,首先先要了解一个概念:形参≠实参
当我们在提到形参和实参的时候,一般都是在参数传递的时候。
在C++中,默认的参数传递的方式是值传递:即在函数调用的时候,编译器将传入的参数(实参)复制一份给形参,简单来说就是拷贝
我们通过一个简单的例子去理解
void func(int x, int y) { x = 100; y ++; } int main() { int a = 10, b = 1; func(a, b); return 0; }
在上面的例子中,传入的a和b的值就是我们说的实参,而x和y的值就是我们说的形参
通常的理解就是:传入a、b就是将a给x,将b给y。然而深入理解这种说法并不准确,稍微准确的说法是:传入a、b就是将a复制一份给x,将b复制一份给y,也就是我们说的值传递
这两种说法有什么区别呢??
如果使用第一种说法,很容易让人理解为,a的值就直接赋给x,b的值就直接赋给b了,这种说法是错误的!!!
这就和我们之前提到的形参≠实参联系起来了
形参≠实参
从概念来看
-
实参:函数被调用时,传给函数的具体的值,具体的变量(“真实数据”)
-
示例:int result = sum(3, 5); 中的 3 和 5 就是实参;int a=2, b=4; sum(a, b); 中的 a 和 b 也是实参
-
-
形参:可以通俗理解为占位符,用来确定函数要接受的值的类型、个数……
-
示例:int sum(int x, int y) 中的 x 和 y 就是形参,它们只是告诉编译器:这个sum函数需要接收两个int类型的参数,至于x和y具体是什么值,只有函数被调用时才知道
-
从作用域来看
-
实参的作用域在调用者的上下文中(比如在main函数中调用其他函数,实参的作用域就是main函数或更外层)
-
形参的作用于只在函数中,当函数调用结束,形参就会自动销毁,其本质是函数内部的局部变量
从创–销过程来看
C++ 程序执行时,函数调用会涉及 栈帧(Stack Frame)的创建与销毁(栈是 C++ 用于存放局部变量、函数参数的默认内存区域,先进后出),实参和形参的生命周期完全不同,我们以值传递(默认传参方式)为例
-
在函数定义的过程中,形参仅仅只是声明,并不直接创建。假设代码如下所示:
void add(int a, int b) { return a + b; }
在上面的定义中,形参a和b仅仅是定义,并非创建
当我们写好一个函数定义时,编译器只会做两件事:
记录函数的接口信息(函数名、返回值类型、形参的类型 / 数量 / 顺序)
检查形参的语法合法性(必须指定类型,不能重复定义等)
此时,形参x和y并没有被分配内存,也不存在于程序的内存空间中,它只是一个 “语法上的约定”,相当于你在纸上写了 “我需要两个整数”,但还没有实际拿到这两个整数。
-
在函数调用之前,实参就已经存在。实参的创建和生命周期,与被调用函数无关,只依赖于它自身的定义上下文(比如a在main函数中定义,生命周期就从a定义开始,到main函数执行结束为止)。
int main() { int a = 3; int b = 5; int res = add(a, b); cout << res << endl; }
上面的代码中,
实参在调用add()函数前实参a和b已经被创建(在main函数的栈帧中分配了内存,存储了值3和5)
如果实参是表达式(如sum(3+2, b*1)),编译器会先对表达式进行求值,得到一个确定的结果(5和5),这个结果才是最终要传递的 “真实实参”;
实参的类型会被编译器检查,确保与形参类型兼容(如int实参可以传递给int形参,double实参可以隐式转换为int形参等),不兼容则编译报错。
-
在函数调用时,形参正式创建(分配内存),完成实参→形参的 “值拷贝”
程序为被调用函数(sum)创建一个独立的函数栈帧(栈区中一块专属内存区域,用于存放该函数的形参、局部变量、返回地址等信息);
在这个新创建的栈帧中,为形参x和y分配内存(此时形参才真正 “诞生”);
执行值拷贝操作:将实参a的值(3)拷贝到形参x的内存空间中,将实参b的值(5)拷贝到形参y的内存空间中;
拷贝完成后,实参和形参就没有任何关联了—— 它们位于内存中的不同位置(实参在main函数栈帧,形参在sum函数栈帧),存储了相同的值,但却是两个完全独立的内存单元,相当于 “复印文件”:实参是原件,形参是复印件,复印件的修改不会影响原件。
为什么不实现形参和实参的“大一统”
了解了形参和实参之后,我就在思考:为什么既要引入实参,又要引入形参呢??这样每次学习不是很麻烦吗,为什么不直接统一成一个变量来实现呢??
实际上,若“统一”了形参和实参,可能带来的麻烦可能更多!!!
如果统一形参和实参(共享同一块内存),会存在致命问题
-
函数内部对参数的任何修改,都会直接同步到外部实参,开发者很容易在无意识中破坏外部数据。这就是编程中的副作用:函数执行时,除了返回预期结果外,意外修改了函数外部的变量(即实参),这种隐式修改在大型程序中是灾难性的。区分形参和实参的首要原因就是隔离副作用
-
示例:一个本应只做 “求和计算” 的函数,若不小心写了a = 0;(统一参数场景下),外部的实参a就会被永久改为 0,后续代码使用a会全部出错。尤其在多人协作的大型项目中,函数可能被数百次复用,无法保证每个开发者都能避免修改参数,这种「隐式 bug」极难排查,调试成本极高。
-
除了上面的缺点之外,如果统一了形参和实参,那么每次调用函数时,不仅要关注函数的返回值,还要时刻担心 “函数会不会修改我的实参?”,程序的行为变得混乱,违背了「最小意外原则」。
区分形参和实参(值传递分离)的核心优势
形参是实参的「独立副本」,函数内部的所有操作都被限制在函数自身的栈帧内,与外部实参完全隔离,实现了「函数内部逻辑与外部数据的解耦」。
-
安全:函数无论如何修改形参,都不会影响外部实参,从根源上杜绝了 “参数被意外修改” 的副作用;
-
可预测:调用函数时,只需要关注 “实参的输入值” 和 “函数的返回值”,无需关心函数内部的实现细节,程序的行为更符合开发者的预期。
这是 C 语言设计者的核心取舍:牺牲一点点语法简洁性,换取程序的高安全性和可预测性,这对于要求稳定性极高的系统级编程至关重要。
若统一形参和实参,会打破模块化
函数必须知晓外部实参的具体细节(内存地址、作用域、生命周期等),才能正常操作。比如,函数需要关心实参是局部变量还是全局变量、实参的内存是否有效、是否会被其他模块修改,这导致函数无法独立存在,与外部代码强绑定,模块耦合度过高。
一个针对某个实参编写的函数,无法直接复用在其他实参上(因为需要适配不同实参的内存细节),无法实现代码复用失去了函数的核心价值。
区分形参和实参(值传递分离)的核心优势
函数只需要关心「形参的类型和值」,不需要关心实参的来源(常量、变量、表达式)、存储位置(main 栈帧、其他函数栈帧)、生命周期(何时创建、何时销毁),从而能实现函数与外部数据解耦
示例:int sum(int x, int y)函数,无论传递sum(3,5)(常量)、sum(a,b)(变量)、sum(3+2, b*1)(表达式),都能正常工作,因为它拿到的是独立的拷贝值,与实参的具体形态无关。
函数一旦编写完成,就可以作为一个独立的「黑盒」在任何场景下复用(实现黑盒复用),无需修改内部代码,只需要传递符合形参类型要求的实参即可。这是大型项目开发的基石 —— 比如 C 标准库中的sqrt()(求平方根)、printf()等函数,能被全球开发者复用,正是因为它们的参数设计是「隔离外部数据」的。


![P11246 [GESP202409 六级] 小杨和整数拆分-171主机测评](https://www.171host.com/wp-content/uploads/2026/11/20261108100631-6af049a75855c-220x150.png)

