C++ 函数参数传递:传值,传指针,传引用

PS:首先理解形参   实参概念。形参是在函数定义的括号内定义的专用变量,它们的目的是保存按实参传递给它们的信息,实参被列在函数调用语句的括号内。

int func(int x)//x是形参
{
    return x*x;
}
int main(void)
{
    int a = 3;
    func(a);//a是实参
    return 0;
} 

上面的代码中,x是形参,a是实参。形参x是实参a的一个拷贝

1.C++    按值传递

采用按值传递的方式,当信息被传递给一个函数时,这意味着形参接收的是传递给它的值的副本。如果形参的值在函数内部被改变,那么它对原始实参是没有影响的。

#include <iostream>
using namespace std;
// 函数声明
void changeMe(int aValue);
int main()
{
    int number = 12;
    // 输出number的值               此处输出为  12
    cout << "In main number is " << number << endl;
    //调用changeMe()函数,将number中的值作为参数传递
    changeMe(number);                   //此处调用函数内部有一个输出为   0
    // 调用函数之后,再次输出number的值          此处输出为  12
    cout << "Back in main again, number is still " << number << endl;
    return 0;
}
void changeMe(int myValue)
{
    //改变 myValue 的值为 0
    myValue = 0;
    //输出 myValue的值             此处输出为  0
    cout << "In changeMe, the value has been changed to " << myValue << endl;
}

程序输出结果:

In main number is 12
In changeMe, the value has been changed to 0
Back in main again, number is still 12

当函数原型列出变量名称和数据类型时,它使用的名称只是虚拟名称,编译器实际上并不使用它们,并且不必在函数头中使用一致的名称。第 5 行中的 changeMe 函数原型和第 19 行中的 changeMe 函数头都指定该函数具有一个 int 形参,但它们使用了不同的名称。
并且,即使在 changeMe 函数中形参变量 myValue 已更改,实参 number 也不会被修改。这是因为 myValue 变量只包含了 number 变量的副本。只是这个副本被改变,而不是原来的。changeMe 函数无法访问原始实参。图 1 说明了形参变量在内存中的存储位置与原始实参的存储位置是分开的。

2.C++    指针传递

传指针就是把实参的地址传递给函数。传指针可以修改实参的值,在C++里也不会存在调用对象的拷贝构造函数的问题, 传指针的效率比传值要高。所以,如果需要修改实参的值,就不能传值,而需要传指针等。但是,传指针比传值复杂,指针计算一旦移动出了正常范围,会造成程序的非法访问等。

void func(int *x)//func采用了传指针的形式
{
    *x = *x+1;
    printf("*x=%d\n", *x);
}
int main(void)
{
    int a = 0;
    func(&a);//把实参a的地址传递给了函数func
    printf("a=%d\n", a);
    return 0;
}

分析:传指针可以修改实参的值。根据指针的定义,*x就是a,所以,*x=*x+1,即为a = a+1,所以上面的代码输出结果为:
*x=1
a=1

C/C++ 禁止在函数调用时直接传递数组的内容,而是强制传递数组指针

而对于结构体和对象没有这种限制,调用函数时既可以传递指针,也可以直接传递内容;为了提高效率,我曾建议传递指针,这样做在大部分情况下并没有什么不妥,读者可以点击《C语言结构体指针》进行回顾。

在 C++ 中,我们有了一种比指针更加便捷的传递聚合类型数据的方式,那就是引用(Reference)

3.C++   引用传递

引用介绍参照博客https://blog.csdn.net/m0_37957160/article/details/104705220

前面讲过,实参通常是通过值传递给函数的,这意味着形参接收的只是发送给它们的值的副本,它们存储在函数的本地内存中。对形参值进行的任何更改都不会影响原始实参的值。然而,有时候可能会希望一个函数能够改变正在调用中的函数(即调用它的函数)中的一个值,这可以通过引用传递的方式来完成。

我们知道,变量是可以保存数据的内存位置的名称。当使用变量时,实际上就是访问存储在分配给它的内存位置的数据。引用变量是变量的另一个别名,它没有自己的存储数据的内存位置,它访问的是另一个变量的内存位置。对引用变量作出的任何更改,实际上都是对它所引用的变量内存位置中存储数据的更改。

当使用引用变量作为形参时,它将变为实参列表中相应变量的别名,对形参进行的任何更改都将真正更改正在调用它的函数中的变量。当以这种方式将数据传递给形参时,该实参被称为按引用传递。

在定义或声明函数时,我们可以将函数的形参指定为引用的形式,这样在调用函数时就会将实参和形参绑定在一起,让它们都指代同一份数据。如此一来,如果在函数体中修改了形参的数据,那么实参的数据也会被修改,从而拥有“在函数内部影响函数外部数据”的效果。

引用变量的定义方法和常规变量类似,但是其数据类型和名称之间有一个 & 符号。例如,以下函数定义使形参 refVar 成为引用变量:

void doubleNum(int& refVar)
{
    refVar *= 2;
}

注意,变量 refVar 被称为“对 int 的引用”。
该函数将 refVar 乘以 2,因为 refVar 是引用变量,所以该操作实际上将对作为实参传递给函数的变量执行。
具有引用形参的函数的原型也必须具有 & 符号。与函数头一样,它在数据类型和变量名之间。如果原型中省略了变量名,那么 & 符号将跟在数据类型后面。以下所有 doubleNum 函数的原型都是正确的:

void doubleNum(int &refVar);
void doubleNum(int& refVar);
void doubleNum(int &);
void doubleNum(int&);

注意,& 符号必须出现在使用引用变量作为形参的任何函数的原型和函数头中。它不会出现在函数调用中。

#include <iostream>
using namespace std;
// Function prototype. The parameter is a reference variable.
void doubleNum(int SrefVar);
int main()
{
    int value = 4;
    cout << "In main, value is " << value << endl;      //输出4
    cout << "Now calling doubleNum..." << endl;
    doubleNum(value);        
    cout << "Now back in main, value is "<< value << endl;             //输出4
    return 0;
}
void doubleNum (int SrefVar)
{
    refVar *= 2;
}

程序输出结果:

In main, value is 4
Now calling doubleNum...
Now back in main, value is 8

此程序中的形参 refVar “指向”函数 main 中的 value 变量。当程序使用一个引用变量时,它实际上是使用它所引用或指向的变量,(引用变量实际上指向的是被它引用的变量)

当函数的目的是接收调用它的函数存储在变量中的输入值时,使用引用变量作为函数 形参特别有用。另外,引用形参还可以用于必须从函数发回多个值的情形。如果函数将计算并发回单个值,通常认为使用返回值的函数更合适,可以使用 return 语句返回值。

下面程序是对之前程序的修改。它添加了一个函数 getNum,该函数接收用户的输入并将其存储在 userNum 中,但是,形参 userNum 是对 main 的变量 value 的引用,所以这是实际存储输入数据的位置。

#include <iostream>
using namespace std;

//Function prototypes
void getNum(int &);
int doubleNum(int);

int main()
{
    int value;
    // Call getNum to get a number and store it in value
    getNum(value);
    value = doubleNum(value);                  //调用函数后,将value变为24
    // Display the resulting number
    cout << "That value doubled is " << value << endl;     //最后输出 24
    return 0;
}
void getNum(int &userNum)
{
    cout << "Enter a number: ";
    cin >> userNum;                        //输入12
}
int doubleNum (int number)
{
    return number *2;
}

程序输出结果:

Enter a number: 12
That value doubled is 24

注意,只有变量才能按引用传递。如果尝试将非变量实参(例如常数、常量或表达式)传递到引用形参中,将导致错误。

如果一个函数有多个形参是引用变量,则必须在原型和函数头中为每个形参使用 & 符号。以下是使用 4 个引用变量形参的函数的原型:

void addThree(int& num1, int& num2, int& num3, int& sum);

以下是函数定义:

void addThree(int& numl, int& num2, int& num3, int& sum)
{
    cout << "Enter three integer values: ";
    cin >> num1 >> num2 >> num3;
    sum = num1 + num2 + num3;
}

但是请注意,addThree 函数只需要一个引用形参 sum,其他 3 个形参可以通过值接收它们的实参,因为在此处的函数中没有改变它们。

提示:应该仅在绝对需要时才使用引用变量。任何时候允许一个函数来改变函数之外的变量,其实都是在创建潜在的调试问题。

文章转自C语言中文网:http://c.biancheng.net/view/2251.html

引用的本质:

在定义或声明函数时,我们可以将函数的形参指定为引用的形式,这样在调用函数时就会将实参和形参绑定在一起,让它们都指代同一份数据。如此一来,如果在函数体中修改了形参的数据,那么实参的数据也会被修改,从而拥有“在函数内部影响函数外部数据”的效果。

总之:传值不能修改实参,且如果是对象,效率较低;传指针能够修改实参,效率较高,但容易出错;传引用能够修改实参,效率较高,而且不易出错。

一个能够展现按引用传参的优势的例子就是交换两个数的值,如下所示:

#include <iostream>
using namespace std;

void swap1(int a, int b);
void swap2(int *p1, int *p2);
void swap3(int &r1, int &r2);


int main() {
    int num1, num2;
    cout << "Input two integers: ";
    cin >> num1 >> num2;
    swap1(num1, num2);
    cout << num1 << " " << num2 << endl;    //输出与输入相同,因为值传递,不能改变原始数据

    cout << "Input two integers: ";
    cin >> num1 >> num2;
    swap2(&num1, &num2);
    cout << num1 << " " << num2 << endl;    //输出与输入相反,指针传递可以改变原始数据

    cout << "Input two integers: ";
    cin >> num1 >> num2;
    swap3(num1, num2);
    cout << num1 << " " << num2 << endl;     //输出与输入相反,因为引用传递,可以改变原始数据

    return 0;
}

//直接传递参数内容
void swap1(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

//传递指针
void swap2(int *p1, int *p2) {
    int temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}

//按引用传参
void swap3(int &r1, int &r2) {
    int temp = r1;
    r1 = r2;
    r2 = temp;
}

运行结果:
Input two integers: 12 34↙
12 34
Input two integers: 88 99↙
99 88
Input two integers: 100 200↙
200 100

本例演示了三种交换变量的值的方法:
1) swap1() 直接传递参数的内容,不能达到交换两个数的值的目的。对于 swap1() 来说,a、b 是形参,是作用范围仅限于函数内部的局部变量,它们有自己独立的内存,和 num1、num2 指代的数据不一样。调用函数时分别将 num1、num2 的值传递给 a、b,此后 num1、num2 和 a、b 再无任何关系,在 swap1() 内部修改 a、b 的值不会影响函数外部的 num1、num2,更不会改变 num1、num2 的值。

2) swap2() 传递的是指针,能够达到交换两个数的值的目的。调用函数时,分别将 num1、num2 的指针传递给 p1、p2,此后 p1、p2 指向 a、b 所代表的数据,在函数内部可以通过指针间接地修改 a、b 的值。我们在《C语言指针变量作为函数参数》中也对比过第 1)、2) 中方式的区别。

2) swap3() 是按引用传递,能够达到交换两个数的值的目的。调用函数时,分别将 r1、r2 绑定到 num1、num2 所指代的数据,此后 r1 和 num1、r2 和 num2 就都代表同一份数据了,通过 r1 修改数据后会影响 num1,通过 r2 修改数据后也会影响 num2。

从以上代码的编写中可以发现,按引用传参在使用形式上比指针更加直观。在以后的 C++ 编程中,我鼓励读者大量使用引用,它一般可以代替指针(当然指针在C++中也不可或缺),C++ 标准库也是这样做的。

 

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中有多种方式可以递参数,包括传值引用引用指针。它们之间有一些区别和适用场景。 1. 传值(pass by value):将参数的副本递给函数。在函数内对参数的修改不会影响到原始值。 ```cpp void func(int num) { num = 10; // 对参数进行修改 } int main() { int num = 5; func(num); // 传值调用 cout << num; // 输出原始值5 return 0; } ``` 传值适用于需要在函数内部进行修改,但不希望影响到原始值的情况。 2. 引用(pass by reference):通过引用递参数,函数内对参数的修改会直接影响到原始值。 ```cpp void func(int &num) { num = 10; // 对参数进行修改 } int main() { int num = 5; func(num); // 引用调用 cout << num; // 输出修改后的值10 return 0; } ``` 引用适用于需要在函数内部修改参数,并且希望这些修改对原始值产生影响的情况。 3. 引用(pass by const reference):通过常引用递参数,函数内不能对参数进行修改。 ```cpp void func(const int &num) { // num = 10; // 错误,不能修改常引用参数 } int main() { int num = 5; func(num); // 引用调用 cout << num; // 输出原始值5 return 0; } ``` 引用适用于需要在函数内部对参数进行只读操作,不希望对原始值产生任何修改的情况。 4. 指针(pass by pointer):通过指针递参数,可以在函数内对参数进行修改,并且可以递空指针。 ```cpp void func(int *ptr) { *ptr = 10; // 对参数进行修改 } int main() { int num = 5; func(&num); // 指针调用 cout << num; // 输出修改后的值10 return 0; } ``` 指针适用于需要在函数内部对参数进行修改,并且可能需要递空值的情况。 总结: - 传值:适用于不需要对原始值产生影响的情况。 - 引用:适用于需要在函数内部对参数进行修改,并且希望这些修改对原始值产生影响的情况。 - 引用:适用于需要在函数内部对参数进行只读操作,不希望对原始值产生任何修改的情况。 - 指针:适用于需要在函数内部对参数进行修改,并且可能需要递空值的情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值