问题

求助:C++中应该怎样理解实参和形参的区别?

回答
好,咱们不绕弯子,直接切入正题。在C++里,说到函数,离不开实参和形参这两个概念,它们就像是函数的“输入口”和“占位符”。理解它们俩的区别,是掌握函数传值、传址等核心机制的关键。

咱们先从最直观的来说,把它们想象成我们在生活中接收信息和处理信息的过程。

形参(Formal Parameter):函数的“占位符”或“模板”

想象一下,你正在写一份食谱,上面写着:“将 [食材] 切成小块,加入 [调味料],然后 [烹饪方式]。”

这里的 `[食材]`、`[调味料]` 和 `[烹饪方式]` 就是形参。它们是你为了描述一个通用过程而设定的占位符,它们还没有具体的值,只是告诉你这个位置需要接收什么类型的信息。

在C++里,形参就是在你定义函数的时候,写在函数名后面括号里的那些变量名。它们就像是函数的“入场券”,声明了函数在被调用时,期望接收什么样的数据类型。

特点:
是变量: 形参本身就是函数内部的局部变量。
定义时出现: 只在函数定义的时候出现,用于描述函数的功能和需要的输入。
没有具体值(初始状态): 在函数被调用之前,形参本身是没有值的。它们的值会在函数被调用时,从实参那里获得。
类型明确: 定义形参时,必须指定它的数据类型(比如 `int`, `double`, `std::string`, 或者某个类名)。

举个例子:

```c++
void printMessage(std::string message, int count) { // message 和 count 就是形参
for (int i = 0; i < count; ++i) {
std::cout << message << std::endl;
}
}
```

在这段代码里:
`std::string message` 是一个形参,它告诉我们这个函数期望接收一个字符串。
`int count` 是另一个形参,它告诉我们这个函数期望接收一个整数。

在你写 `printMessage` 函数的时候,你并不知道具体要打印什么内容,也不知道要打印多少次。`message` 和 `count` 就是帮你预留的位置。

实参(Actual Parameter):函数的“具体数值”或“实际材料”

接着上面的食谱例子,当你的朋友想根据你的食谱做菜时,他就会拿出具体的食材、调味料和选择一种烹饪方式。比如:

“我要用西红柿作为[食材],加盐和糖作为[调味料],然后用炒的[烹饪方式]。”

这里的 “西红柿”、“盐和糖” 以及 “炒” 就是实参。它们是实际的值,是用来填充形参位置的具体数据。

在C++里,实参就是在你调用函数的时候,传递给函数的那些实际的值或者包含这些值的变量。它们是真正会被传递到函数内部,供形参使用的“原材料”。

特点:
是具体的值或表达式: 实参可以是字面量(如 `5`, `"Hello"`)、变量(如 `myNumber`, `userName`),甚至是表达式(如 `x + y`, `calculateValue()`)。
调用时出现: 只在函数被调用的时候出现,放在括号里,顺序与形参一一对应。
有具体值: 实参必须有一个具体的值,这个值会被复制或引用传递到函数内部。
类型需要匹配(或可隐式转换): 实参的类型应该与对应的形参类型兼容。如果不兼容,编译器可能会尝试进行隐式类型转换,但过度依赖隐式转换容易出错。

举个例子:

```c++
int main() {
std::string myFavorite = "学习C++真有趣!";
int timesToPrint = 3;

// 调用 printMessage 函数,myFavorite 和 timesToPrint 就是实参
printMessage(myFavorite, timesToPrint);

// 也可以直接传递字面量作为实参
printMessage("你好,世界!", 2);

return 0;
}
```

在这段 `main` 函数里:
第一次调用 `printMessage(myFavorite, timesToPrint);` 时:
`myFavorite`(值为 `"学习C++真有趣!"`)作为实参,传递给形参 `message`。
`timesToPrint`(值为 `3`)作为实参,传递给形参 `count`。
第二次调用 `printMessage("你好,世界!", 2);` 时:
`"你好,世界!"`(字面量)作为实参,传递给形参 `message`。
`2`(字面量)作为实参,传递给形参 `count`。

核心区别总结:形参是“影子”,实参是“本体”

用更精炼的比喻来说:

形参就像是一面镜子的“位置”,它告诉你“这里需要照出东西来”。
实参就是你站在镜子前面,“你这个人”本身,是你实际的影像。

当你调用函数时,就是你带着“本体”(实参)去填补镜子里的“位置”(形参)。

传值(Pass by Value)与传引用/指针(Pass by Reference/Pointer)

理解了形参和实参,我们就能更深入地理解C++中常见的两种函数参数传递方式:

1. 传值(Pass by Value):
这是C++默认的参数传递方式。
当实参传递给形参时,实参的值会被复制一份,然后这个复制品被赋给形参。
在函数内部,形参是实参的一个独立的副本。修改形参不会影响到原来的实参。
这就是为什么我们说形参是实参的“影子”或“副本”。

```c++
void modifyValue(int num) { // num 是形参,接收 num 的一个副本
num = num 2; // 只修改了副本,不会影响 main 函数里的 originalValue
std::cout << "Inside function: " << num << std::endl;
}

int main() {
int originalValue = 10;
std::cout << "Before function call: " << originalValue << std::endl; // 输出 10
modifyValue(originalValue); // originalValue 的值被复制给 num
std::cout << "After function call: " << originalValue << std::endl; // 输出仍是 10
return 0;
}
```

2. 传引用(Pass by Reference)或 传指针(Pass by Pointer):
在这种方式下,传递给形参的不是实参的值,而是实参本身的地址(通过引用或指针)。
形参变成了实参的“别名”(引用)或者指向实参的“指示器”(指针)。
因此,在函数内部通过形参对实参进行任何修改,都会直接影响到原来的实参。

传引用示例:
```c++
void modifyValueByRef(int& num) { // num 是对实参的引用
num = num 2; // 直接修改实参的值
std::cout << "Inside function (by ref): " << num << std::endl;
}

int main() {
int originalValue = 10;
std::cout << "Before function call: " << originalValue << std::endl; // 输出 10
modifyValueByRef(originalValue); // originalValue 本身(或者说它的引用)被传递给 num
std::cout << "After function call: " << originalValue << std::endl; // 输出变成 20 了!
return 0;
}
```
传指针示例:
```c++
void modifyValueByPtr(int numPtr) { // numPtr 是一个指针,指向实参
if (numPtr) { // 检查指针是否为空
numPtr = (numPtr) 2; // 解引用指针,修改实参的值
}
std::cout << "Inside function (by ptr): " << (numPtr ? numPtr : 0) << std::endl;
}

int main() {
int originalValue = 10;
std::cout << "Before function call: " << originalValue << std::endl; // 输出 10
modifyValueByPtr(&originalValue); // 传递 originalValue 的地址给 numPtr
std::cout << "After function call: " << originalValue << std::endl; // 输出变成 20 了!
return 0;
}
```

为什么要区分它们?

1. 控制副作用: 传值可以防止函数内部修改外部变量,保证代码的独立性和可预测性。当你只需要函数使用某个值进行计算,而不需要改变它的原始状态时,传值是最安全的选择。
2. 效率和性能: 对于大型对象(如复杂的结构体或字符串),传值会复制整个对象,这可能非常耗费时间和内存。此时,传引用或指针(只复制地址)就显得更有效率。
3. 修改外部数据: 如果你的函数设计目的是要修改调用者提供的某个变量的值,那么传引用或指针是必须的。
4. 接口设计: 函数的形参定义是它对外提供的接口的一部分。明确的形参类型和传递方式,有助于其他人理解如何正确地调用你的函数。

简单来说:

形参: 是函数内部使用的变量,是声明函数需要什么样子的输入。
实参: 是函数被调用时,你实际传递给函数的具体数据。

它们的关系就是:实参的值(或地址)被传递给形参,使得形参在函数内部获得对应的状态。

理解这一点,再去看`const`修饰符的作用时会更清晰。比如 `void func(const int& value)`,这里 `value` 是形参,它的类型是 `int` 的常量引用。当调用 `func(myVar)` 时,`myVar` 就是实参。这个常量引用告诉我们,在 `func` 函数内部,不能通过 `value` 来修改 `myVar` 的值。

希望这些解释能够帮你彻底弄清楚实参和形参的区别。有什么不清楚的地方,随时可以再问!

网友意见

user avatar

形参和实参的概念其实没有意义。

       // x, y 形参 int max(int x, int y); // a, b 实参 max(a, b);     

现实中,根本没人说形参x,y,实参a,b,都统一用参数x,y,参数a,b表示。

英语有时候x,y会用parameter,也就是形参, a,b会用argument,也就是实参。但实际上大部分人也是混用的,有时候无论形参实参全用parameter,有时候全用argument,都代表参数。

形参和实参这两个词如果你全部用参数代替,也不会有任何歧义。给定代码,参数x,y,参数a,b,你是马上就知道他指的是什么,根本就不需要形参和实参来区分。并且区分出来其实也没什么意义。。

所谓形参和实参互不影响,这种书就不要读了。形参和实参的概念稍微提一下知道就行了,都没必要深入的东西,要强求新手去理解他背后的汇编机器代码(为何形参和实参互不影响),简直是反人类。这种没用的规则放弃就好了,没必要背,编程多了,再回来看这句话,自然就理解他什么意思了。

user avatar

我一直认为,在C/C++里,“形参”和“实参”这两个概念是完全没必要的凭空捏造的垃圾概念。

事实上,从CPU架构和指令的角度来说,无论是寄存器传参还是压栈传参,传参就只有一种形式:传值。所区别的无非是这个“值”是实际的值还是指针的值(地址)而已——你把指针理解了,自然就理解了。

如果有些高级语言废除了指针的概念,那弄点其它概念去绕也行。但是对于C/C++,一句话就说清楚的事,根本没必要绕这些。


再补充一些:如果不用形参、实参之类的垃圾概念,怎么解释你的例子。

首先明确一点,你的两个例子两个函数的四个参数里,只有那个A是传了指针的,其它都是传数值。如果你能理解指针的概念,你就知道为什么A在函数里改了在外面也会变,而其他的不变了。

剩下的问题可能就是:为什么参数A传的是指针?这就是纯语法方面的问题了。如果你想学得仔细点的话,可以学习一下隐式转换相关的内容,尤其是你这里涉及的指针和数组之间隐式转换。如果不想弄那么仔细,也没问题,对于语法类问题,基本上都可以简单粗暴的俩字回答:规定。


我再解释一下形参这概念到底有多坑爹吧:

首先,你要说形参实参这概念没有呢,倒也不是——但C99的标准文档里面,通篇只有一个地方提到了formal parameter/actual argument这个术语,就是在一开头的术语表里,之后全篇都再也没用过这个术语了。

然后,我再截两个图,让大家看看他这个唯一用到这个术语时的说法:

我相信大家一定认得括号里的那个单词是什么意思的。而且还重复的把formal/actual和argument/parameter排列组合的全列一遍,说明基本上两者是混来混去的用的。

实际上,自己读一下里面的内容就知道了,这种东西你硬要分,那就只能是在词法语法上去区分的。你要是搞编译器前端,做什么语法分析的时候,去仔细读一下这两段东西,有用。但是对于99%一辈子不接触编译器前端的C/C++开发人员来说,就是毫无意义的。至于说在初学者中间普及甚至强调这种晦涩概念,那更是扯蛋。

类似的话题

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有