形参和实参是函数调用中不同阶段的角色:形参是定义时的占位符,实参是调用时传入的值;三类形参(int、int&、int*)决定是否可修改实参,类型匹配依赖隐式转换,未命名形参仍需提供对应实参。

形参和实参不是“两个参数”,而是函数调用过程中**不同阶段、不同作用域的两个角色**:形参是函数定义时预留的“占位符”,实参是调用时真正塞进去的值。能不能改外面的变量,不取决于“叫什么”,而取决于形参怎么声明——int、int&、int* 这三种写法,行为天差地别。
形参只是函数体内的局部变量,生命周期由调用决定
形参在函数被调用时才分配栈空间,函数返回就立刻释放。它不是全局变量,也不是静态变量,更不是实参的“影子”或“映射”。它就是一个独立对象,靠实参来初始化。
-
int f(int x)中的x是一个普通局部变量,哪怕你写成int x = 42;效果也一样 - 实参可以是字面量(如
f(5))、表达式(如f(a + b))、临时对象(如f(std::string("hello"))),只要能初始化x就行 - 没有默认值的形参,调用时必须提供对应实参;否则编译报错
error: no matching function
实参求值顺序未定义,但初始化顺序固定
C++ 标准不规定多个实参的求值顺序。比如 f(g(), h()) 中,g() 可能先执行,也可能后执行——这跟形参声明顺序无关,只影响副作用逻辑。
- 但形参的初始化顺序永远按声明顺序:
void f(int a, int b)中,a一定先用第一个实参初始化,b再用第二个 - 如果实参有副作用(比如修改全局变量、打印日志),且依赖彼此结果,就可能出问题
- 常见坑:
int i = 0; f(i++, i++)—— 实参求值顺序不确定,i最终可能传入0,1或1,1等,行为未定义
int、int&、int* 这三种形参声明,决定了实参是否可被修改
所谓“传值/传引用/传指针”,本质是形参类型不同,导致初始化方式不同。C++ 没有“传递模式”关键字,只有类型系统在起作用。
-
void f(int x):用实参的值拷贝构造x,改x不影响实参 -
void f(int& x):x是实参对象的别名,改x就是直接改实参(要求实参必须是左值,比如变量a,不能是字面量5) -
void f(int* x):x是指针类型,接收的是地址值(比如&a),解引用后才能改实参;空指针传入会导致未定义行为 - 右值引用
int&&能绑定临时对象,用于移动语义,但不能绑定普通变量(除非用std::move显式转换)
容易忽略的关键点:类型匹配是隐式转换,不是强制赋值
实参类型不需要和形参完全一致,只要能隐式转换过去就行。但这个“能转换”常埋雷。
-
void f(double x)接收int y = 42;→f(y)合法,y被提升为double - 反过来
void f(int x)接收double y = 3.14;→f(y)会截断小数部分,编译器可能只给警告(conversion from 'double' to 'int', possible loss of data) - 用户自定义类型若定义了转换构造函数或类型转换操作符,也会触发隐式转换,可能造成意外拷贝或逻辑绕过
- 用
explicit修饰构造函数,就能阻止这种隐式转换,让意图更明确
最常被忽略的其实是:形参有没有名字、是不是用到了,完全不影响调用约定。未命名形参(如 void f(int, int))只是告诉编译器“我需要两个 int,但不打算用它们”,实参仍需提供,类型和数量一个都不能少。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











