-
2020-08-01 06:54:01
函数调用者如何将参数传递给被调用者是有讲究的。 总的来说,函数参数传递分为3种情况:传值,传指针和传引用。
首先,理解一下实参与形参的概念。
int func(int x)//x是形参
{
return x*x;
}
int main(void)
{
int a = 10;
func(a);//a是实参
return 0;
}
上面的代码中,x是形参,a是实参。形参x是实参a的一个拷贝。&a和&x完全不同。一,传值
所谓传值,顾名思义,就是把实参的值直接传递给函数。因为形参是实参的拷贝,所以传值无法改变实参。在C++里面,如果传递的是对象, 那么,在传值过程中,还会隐式的调用对象的拷贝构造函数,有一定的计算执行开销(相当于创建了一个临时对象,函数调用完成后执行临时对象的析构函数)。
void func(int x)//func采用了传值的形式
{
x = x+1;
printf("x=%d\n", x);
}
int main(void)
{
int a = 0;
func(a);//传值不能修改a的值
printf("a=%d\n", a);
return 0;
}
分析:上面的程序采用了传值的参数传递形式,把a的值0传递给了func函数,而由于x是a的一个拷贝(&a和&x完全不同),因此,x=x+1值修改了x的值 并没有修改a的值。所以上面程序执行的结果,输出为:
x=1
a=0二,传指针
传指针就是把实参的地址传递给函数,该地址指向的位置所存储的内容,返回后,该地址的内容发生变化,但不能修改该地址本身,或者让指针指向其他位置。传指针可以修改实参的值,在C++里也不会存在调用对象的拷贝构造函数的问题, 传指针的效率比传值要高。所以,如果需要修改实参的值,就不能传值,而需要传指针等。
但是,传指针比传值复杂,指针计算一旦移动出了正常范围,会造成程序的非法访问等。
void func(int *x)//func采用了传指针的形式
{
*x = *x+1;
printf("*x=%d,x=%p,&x=%p\n", *x,x,&x);
}
int main(void)
{
int a = 0;
func(&a);//把实参a的地址传递给了函数func
printf("a=%d,&a=%p\n", a,&a);
return 0;
}
分析:传指针可以修改实参的值,但不能修改指针本身的值(修改无效)。根据指针的定义,*x就是a,所以,*x=*x+1,即为a = a+1,所以上面的代码输出结果为:
*x=1
a=1传指针,其实也是一种值传递,只不过这个值是一个地址,也就是说如果在调用函数func()中传进去的是变量a的地址0x1234,在函数func()中,实际上是另外临时申请了一个指针变量x,x的值是0x1234,相当于把a的地址赋值给了x,但x本身的地址和a没有任务关系,在函数中,可以修改地址0x1234存储的内容,比如原来是0,可以修改成1,但是你不能把0x1234这个地址给改了,也就是比如你想让x=0x2345,这在函数func()中是没有错的,但是修改后,x的值是0x2345,也就是它指向了另外一个地址,修改的也是0x2345这个位置的内容,就和0x1234没有任何关系了,函数返回后,因为a的地址仍然是0x1234,因为在func()中修改的是0x2345,所以和a没有任何关系。
三,传引用--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;
}
分析:func采用传引用的方法定义,实参a引用传递给函数func之后,func能够修改实参的值。所以上面的程序执行结果为:
x=1
a=1
总之:传值不能修改实参,且如果是对象,效率较低;传指针能够修改实参,效率较高,但容易出错;传引用能够修改实参,效率较高,而且不易出错。更多相关内容 -
Python参数传递机制传值和传引用原理详解
2020-09-16 20:19:26主要介绍了Python参数传递机制传值和传引用原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 -
php传值和传引用的区别点总结
2020-10-15 23:35:12在本篇文章里小编给大家整理的是关于php传值和传引用的区别点总结,需要的朋友们可以参考下。 -
浅析Java方法传值和传引用问题
2020-09-04 20:11:05主要是对Java方法传值和传引用问题进行了详细的介绍,需要的朋友可以过来参考下,希望对大家有所帮助 -
php5.4传引用时报错问题分析
2021-01-20 01:10:34本文实例分析了php5.4传引用时报错问题。分享给大家供大家参考,具体如下: php5.3系列版本以及以前版本,传引用没有什么问题,升级到php5.4以后,传引用的地方,全报错 Fatal error: Call-time pass-by-reference ... -
指针传引用
2021-07-19 17:47:22(通过地址值进行引用然后完成交换) 图右边是代码的在栈帧的的一个交换过程: 大概说一下: 首先初始化的main方法在栈帧 有两个内存空间 分别为0xaa00和0xbb00对应a和b的变量地址 然后运行到调用swap2 产生栈帧空间...
上图中main方法中声明了a和b两个变量,然后通过swap2方法完成值交换。很简单的过程。(通过地址值进行引用然后完成交换)图右边是代码的在栈帧的的一个交换过程:
大概说一下:
- 首先初始化的main方法在栈帧 有两个内存空间 分别为
0xaa00
和0xbb00
对应a和b的变量地址 - 然后运行到调用swap2 产生栈帧空间,空间内 有x和y两个变量形参。
- 然后swap2(&a,&b) 去地址值的方式把a和b的地址给了swap2方法 ,
- swap2方法 的
*x,*y=*y,*x
是做了一个值交换,首先第一个*x
是取内存空间(我们之前说过 :等号左边的变量,代表变量所指向的内存空间
)这里通俗来说就是*x
是去main方法的栈帧下取到a的地址值(0xaa00)。 - 第三个
*y
是根据地址把值取(等号右边的变量,代表变量内存空间存储的数据值
)到然后赋值给*x
(0xaa00),取得的值为20 转化一下:0xaa00=20
一通百通
再来一个demo
主要看
new
,new
这个关键字是去(heap)堆内存申请一块内存空间,下面var p=new(*int)
申请指针类型的内存 ,返回的是一块内存空间的地址值为0xc00001e090
,想要给它赋值就必须进行取到0xc00001e090地址的空间*p,然后赋值*p=&i
,这个时候根据*p
取得值是i的地址0xc0000be008值不是数据值,想要取得i的值需要再加个*
就是**p
,才能取到i的值。绕的一笔func main() { var p =new(*int) //p 0xc00001e090 var i int i=1000 *p=&i fmt.Println(&i) // 0xc0000be008 i地址值 fmt.Println(*p) // 0xc0000be008 i地址值 fmt.Println(p) //0xc00001e090 p变量的内存地址值 fmt.Println(**p) // 1000 i的数据值1000 }
- 首先初始化的main方法在栈帧 有两个内存空间 分别为
-
传值和传引用-综合文档
2021-05-23 03:22:12传值和传引用-我和LabVIEW:一个NI工程师的编程经验 -
C++学习——引用、传值和传引用的效率对比、引用与指针
2020-05-26 16:02:17一、 引用 1.1 引用概念 引用(reference)是为变量起了另一个名字,而不是定义一个新变量。编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。其使用形式如下: 类型& 引用变量名...一、 引用
1.1 引用概念
引用(reference)是为变量起了另一个名字,而不是定义一个新变量。编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。其使用形式如下:
类型& 引用变量名(对象名) = 引用实体;
eg:
int ival = 1024; int &refVal = ival; // refVal 指向ival(是ival的另一个名字) int &refVal2; // 报错:引用必须被初始化
定义引用时,程序把引用和它的初始化绑定在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。因为无法令引用重新绑定到另外一个对象,所以引用必须初始化。注意:引用类型必须和引用实体是同种类型的。
1.2 引用的特性
(1) 引用在定义式必须初始化;
(2)一个变量可以有多个引用;
(3)引用一旦引用一个实体,再不能引用其他实体;
1.3 引用的使用场景
1 做参数
void Swap(int& left, int& right) { int temp = left; left = right; right = temp; }
2 做返回值
int& Count() { static int n = 0; n++; // ... return n; }
注意:如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。
例如下面代码就会出现问题。
int& Add(int a, int b) { int c = a + b; return c; } int main() { int& ret = Add(1, 2); Add(3, 4); cout << "Add(1, 2) is :" << ret << endl; system("pause"); return 0; } 运行结果: Add(1, 2) is :7 请按任意键继续. . .
所以使用时注意作用域。
二、 传值和传引用的效率对比
先用以下代码进行测试一下:
#include <time.h> #include <iostream> using namespace std; struct A { int a[10000]; }; void TestFunc1(A a) {} // 传值 void TestFunc2(A& a) {} // 传引用 A a; A Fun3() { return a; } A &Fun4() { return a; } void test1() { A a; // 以值作为函数参数 size_t begin1 = clock(); for (size_t i = 0; i < 100000; ++i) TestFunc1(a); size_t end1 = clock(); // 以引用作为函数参数 size_t begin2 = clock(); for (size_t i = 0; i < 100000; ++i) TestFunc2(a); size_t end2 = clock(); // 分别计算两个函数运行结束后的时间 cout << "TestFunc1(A)-time:" << end1 - begin1 << endl; cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl; // 以值作为函数返回值 size_t begin3 = clock(); for (size_t i = 0; i < 100000; ++i) Fun3(); size_t end3 = clock(); // 以引用作为函数返回值 size_t begin4 = clock(); for (size_t i = 0; i < 100000; ++i) Fun4(); size_t end4 = clock(); // 分别计算两个函数运行结束后的时间 cout << "Fun3 time:" << end3 - begin3 << endl; cout << "Fun4 time:" << end4 - begin4 << endl; } int main() { test1(); system("pause"); return 0; } 运行结果: TestFunc1(A)-time:129 <-- 参数传值 TestFunc2(A&)-time:2 <-- 参数传引用 Fun3 time:336 <-- 返回值为值 Fun4 time:2 <-- 返回值为引用 请按任意键继续. . .
从运行结果可见传引用比传值的效率高很多。因为,以值做为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。
三、 引用和指针的区别
在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。但是在底层实现上,引用是按指针的方式实现的。怎么体现呢,看汇编代码。如下:
int main() { int a = 10; int& ra = a; ra = 20; int* pa = &a; *pa = 20; return 0; }
将上面简单代码进行反汇编:
可见:
在底层实现上:引用通过指针实现,定义一个引用类型变量相当于定义一个指针类型变量
语法上: 引用是别名,不是指针,没有发生拷贝。
引用和指针的不同点:
1. 引用在定义时必须初始化,指针没有要求
2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
3. 没有NULL引用,但有NULL指针
4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
6. 有多级指针,但是没有多级引用
7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
8. 引用比指针使用起来相对更安全 -
C++传引用和传值,传指针
2018-10-12 18:53:19我们需要传数据而不改变数据存储,直接传值,如int a; 我们需要传数据,并且改变值大小,需要传地址,如 int * pa; 我们需要传数据,并改变数结构中指针的指向,需要传二级指针,如链表中的 node * * l; ... ...我们需要传数据而不改变数据存储,直接传值,如int a;
我们需要传数据,并且改变值大小,需要传地址,如 int * pa;
我们需要传数据,并改变数结构中指针的指向,需要传二级指针,如链表中的 node * * l;
...
这是c语言中传值的形式。参考我之前的一篇文章https://blog.csdn.net/weixin_41143631/article/details/82717506
以前是passl-by-value,而c++在c11中,引入了pass-by-reference,也就是传引用。c++需要一个标识符&。
给出下面代码
void my_swap(int* x, int* y) { *x = *x^*y; *y = *x^*y; *x = *x^*y; cout << "函数中参数x的地址 " << &x << endl; } void my_swap(int&a, int&b) { a = a^b; b = a^b; a = a^b; cout <<"函数中参数a的地址 "<< &a<< endl; } int main() { int www = 1; int a = 0; int b = 100; int x = 0; int y = 100; my_swap(&x, &y); my_swap(a, b); cout << a << " 主函数 a 的地址 "<<&a<<endl; cout << x << " 主函数x的地址 " <<&x<< endl; }
前者swap是传值(生成拷贝,传地址),而后者直接是传引用,形参和实参的本质是一样,引用只是一个别名。
运行结果如下
可以明显看到,这2个重载函数中使用传指针,所指向的地址是不同的,而使用传引用,所指向的地址是相同的。
传引用:被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
传值:被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。
int a=0; //定义a是一个int类变量 int& ra=a; //声明ra是a的“引用”,变量ra具有变量a的地址 //引用不能在引用的基础上再引用。
顺便说一下,gcc在编译时,O2级别优化,传引用,和传地址的汇编代码是一样的。
在微软的vc编译器(vs2013)运行时,直接优化,传引用,和传地址的汇编代码是一样的。
所以在我们写代码时,传引用,传指针都可以,不过我自己也趋向于写引用,在c++的模板中也大量使用引用,从制度上完善代码规范,能防微杜渐。
下面说一下,传引用,和我们常说的“传指针”之间的一些小区别。
A,传引用需要初始化,指针不需要(但指针推荐每次都初始化,即使无值,也初始化为NULL)。
B,使用sizeof对其求大小不一样,引用是对应数据的实际大小,而指针则依据机器固定为相应大小(32位为4字节)。
C,指针更加灵活,++,--可以来访问,而引用没有这些。有多级指针,没有多级引用。
D,引用相当于和被引用者“绑定”,而指针可以不断改变。
...
从安全的角度,引用能替代指针,这个在很多高级语言,不论是解释性语言还是编译语言中都得到了体现。
但维持c++的灵活,我们依然可以使用指针,不过推荐使用引用。
-
传指针和传引用的区别以及指针和引用的区别
2019-03-30 18:21:25引用的定义:引用是给另外一个变量其别名,所以引用不会分配内存空间,引用是引入了对象的一个同义词。 例如: Point pt1(10,10); Point &pt2 = pt1; 上述的代码,定义了pt2为pt1的引用。通过这样的定义,pt... -
传值、传引用、传地址的区别是什么
2019-06-12 16:50:46传值, 是把实参的值赋值给行参 那么对行参的修改,不会影响实参的... 传引用 真正的以地址的方式传递参数 传递以后,行参和实参都是同一个对象,只是他们名字不同而已 对行参的修改将影响实参的值 ---------... -
Python传值还是传引用
2019-04-19 15:09:38Python传值还是传引用 1、Python可变对象与不可变对象 不可变对象:该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址... -
C++中传值传指针传引用的区别
2019-02-28 17:59:28当调用函数时,传递的参数有传值、传指针、传引用这三种形式。 直接传值是直接开辟了一个跟主函数实参一样的空间(地址不一样),里面存放了了跟实参一样大小的值,就相当于数值大小相同但是位置不同。你在这个调用... -
传值,传指针和传引用区别和联系
2016-11-27 12:15:28竟然自己发现对传值,传指针,传引用这个每天都在用的传递方式的区别还不是很清楚。以为自己懂了,其实还理解得还不够深入,基础还需要花时间琢磨。今天参考了很多篇博客和书籍做些总结。其实,不用分为三类,只有两... -
PHP 中传值与传引用的区别。什么时候传值什么时候传引用?
2020-06-12 23:19:02什么时候传值什么时候传引用? 答: 按值传递:函数范围内对值的任何改变在函数外部都会被忽略 按引用传递:函数范围内对值的任何改变在函数外部也能反映出这些修改 优缺点:按值传递时,php必须复制值。特别是... -
42.传值和传引用.doc-综合文档
2021-05-23 17:35:2842.传值和传引用.doc -
js 函数参数传值/传引用
2021-02-25 16:15:29引用类型:严格来说不能算传引用,在js中叫按共享传递 call by sharing也叫按对象传递、按对象共享传递。该策略的重点是:调用函数传参时,函数接受对象实参引用的副本(既不是按值传递的对象副本,也不是按引用传递... -
PHP传值和传引用区别
2018-08-08 14:39:51传值和传引用、传地址的区别: 1、传值,是把实参的值赋值给行参 那么对行参的修改,不会影响实参的值 2、传地址 是传值的一种特殊方式,只是他传递的是地址,不是普通的如int 那么传地址以后,实参和行参都指向同一... -
传引用与传值的区别
2020-03-19 22:51:58传引用:当一个变量的值赋予另外一个变量时,改动新的变量将影响原始的变量;使用引用赋值,简单的就是将&符号加到将要赋值的 变量前;相当于别名; 对象默认是传引用; 使用场景:对于较... -
Java传值调用和传引用调用(参数引用为null的问题)
2019-05-15 20:58:33其实无论是传值还是传引用调用,其本质都是将栈中存储的数据复制一份,传递给栈中的另一个变量。对于基础数据类型,是将数据本身复制一份传递;对于引用类型,是将引用的地址复制一份传递。因此,上述问题可以描述为... -
golang传值和传引用
2019-01-02 09:53:04golang传值和传引用 这里不会解释关于指针的情况,如果读者对C语言或者C++的指针比较了解,那么就能更好地理解本文。 定义 对于代码 modify(a); a.modify(); 如果modify中对于a的修改不会改变传入的a的值,那么就是... -
vue子父组件传引用类型
2020-06-20 21:59:04简单描述一下引用类型复制的特点: 引用类型:把值和地址想成比作面包和面包存放的地址。 复制引用类型的浅复制:A拥有面包和面包的地址,然后将A复制给B,那么B只是知道了面包存放的地址。 复制引用类型的深复制:... -
java中的传值和传引用,什么时候传值,什么时候传引用。
2019-08-07 11:32:04实参: 1.如果是基本数据类型和String,则实参不会改变,因为传的是值。 2.但如果传的是对象集合或者数组,就会改变,因为传的是引用。 -
彻底搞明白传值,传地址,传引用
2020-07-11 22:22:53传引用是真正的以地址的方式传递参数, 传递以后,行参和实参都是同一个对象,只是名字不同而已,对行参的修改将影响实参的值。 4.在python中的情况 python不允许程序员选择采用传值还是传引用。Python参数传递采用... -
C++ 传值和传引用的效率对比
2020-02-09 18:25:35条款22: 尽量用“传引用”而不用“传值” c语言中,什么都是通过传值来实现的,c++继承了这一传统并将它作为默认方式。除非明确指定,函数的形参总是通过“实参的拷贝”(拷贝构造函数)来初始化的,函数的调用者得到... -
C++中vector作为参数的三种传参方式(传值 && 传引用 && 传指针)
2021-02-01 16:09:54vec),传引用 function3(vector *vec),传指针 注意,三种方式分别有对应的const形式,不在此讨论。 三种方式对应的调用形式分别为: function1(vec),传入值 function2(vec),传入引用 function3(&vec),传入... -
Java中的值传递和地址传递(传值、传引用)
2021-02-12 15:09:20首先,不要纠结于 Pass By Value 和 Pass By Reference 的字面上的意义,否则很容易陷入所谓的“一切传引用其实本质上是传值”这种并不能解决问题无意义论战中。更何况,要想知道Java到底是传值还是传引用,起码你要... -
传地址调用和传引用调用的区别
2017-09-05 21:13:17传地址调用和传引用调用的区别相同点: 传引用和传地址,原理上都是将参数变量的地址传递给被调函数。所以在函数内部修改参数的值时,均可返回修改之后的结果给调用者。 不同点: 引用一定会指向一个对象,而指针... -
传值,传地址,传引用傻傻分不清楚
2019-05-16 14:58:34C++语言的传递方式:传值,传地址,传引用。没记错的话C++98之后传引用才可以用。 第一种方式: 传值:就是拷贝一份数据给,数据量小无所谓,数据量大,这个不推荐,效率很慢的。 示例:第一个fun函数。典型的...