C语⾔中函数参数传递的三种⽅式
C语⾔中函数参数传递的三种⽅式
(1)传值,就是把你的变量的值传递给函数的形式参数,实际就是⽤变量的值来新⽣成⼀个形式参数,因⽽在函数⾥对形参的改变不会影响到函数外的变量的值。
(2)传址,就是传变量的地址赋给函数⾥形式参数的指针,使指针指向真实的变量的地址,因为对指针所指地址的内容的改变能反映到函数外,也就是能改变函数外的变量的值。
(3)传引⽤,实际是通过指针来实现的,能达到使⽤的效果如传址,可是使⽤⽅式如传值。
说⼏点建议:如果传值的话,会⽣成新的对象,花费时间和空间,⽽在退出函数的时候,⼜会销毁该对象,花费时间和空间。
因⽽如果int,char等固有类型,⽽是你⾃⼰定义的类或结构等,都建议传指针或引⽤,因为他们不会创建新的对象。
例1:下⾯这段代码的输出结果为:
#include<stdio.h>
void change(int*a, int&b, int c)
{
c=*a;
b=30;
*a=20;
}
int main ( )
{
int a=10, b=20, c=30;
change(&a,b,c);
printf(“%d,%d,%d,”,a,b,c);
return 0;
}
结果:20 30 30
解析:
该题考察函数传参问题。
1,指针传参 -> 将变量的地址直接传⼊函数,函数中可以对其值进⾏修改。
2,引⽤传参 -> 将变量的引⽤传⼊函数,效果和指针相同,同样函数中可以对其值进⾏修改。
3,值传参 -> 在传参过程中,⾸先将c的值复制给函数c变量,然后在函数中修改的即是函数的c变量,然后函数返回时,系统⾃动释放变量c。⽽对main函数的c没有影响。
例2:
#include<stdio.h>
void myswap(int x, int y)
{
int t;
t=x;
x=y;
y=t;
}
int main()
{
int a, b;
printf("请输⼊待交换的两个整数:");
scanf("%d %d", &a, &b);
myswap(a,b); //作为对⽐,直接交换两个整数,显然不⾏
printf("调⽤交换函数后的结果是:%d 和 %d\n", a, b);
printf("调⽤交换函数后的结果是:%d 和 %d\n", a, b);
return 0;
}
#include<stdio.h>
void myswap(int *p1, int *p2)
{
int t;
t=*p1;
*p1=*p2;
*p2=t;
}
int main()
{
int a, b;
printf("请输⼊待交换的两个整数:");
scanf("%d %d", &a, &b);
myswap(&a,&b); //交换两个整数的地址
printf("调⽤交换函数后的结果是:%d 和 %d\n", a, b);
return 0;
}
#include<stdio.h>
void myswap(int &x, int &y)
{
int t;
t=x;
x=y;
y=t;
}
int main()
{
int a, b;
printf("请输⼊待交换的两个整数:");
scanf("%d %d", &a, &b);
myswap(a,b); //直接以变量a和b作为实参交换
printf("调⽤交换函数后的结果是:%d 和 %d\n", a, b);
return 0;
}
第⼀个的运⾏结果:输⼊2 3,输出2 3
第⼆个的运⾏结果:输⼊2 3,输出3 2
第三个的运⾏结果:输⼊2 3,输出3 2
解析:
在第⼀个程序中,传值不成功的原因是指在形参上改变了数值,没有在实参上改变数值。在第⼆个程序中,传地址成功的原因利⽤指针改变了原来的地址,所以实参就交换了。
在第三个程序中,引⽤是直接改变两个实参变量a,b的值,所以就交换了。
下⽂会通过例⼦详细说明关于值传递,指针传递,引⽤传递
1)值传递:
形参是实参的拷贝,改变形参的值并不会影响外部实参的值。从被调⽤函数的⾓度来说,值传递是单向的(实参->形参),参数的值只能传⼊,
不能传出。当函数内部需要修改参数,并且不希望这个改变影响调⽤者时,采⽤值传递。
2)指针传递:
形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本⾝进⾏的操作
3)引⽤传递:
形参相当于是实参的“别名”,对形参的操作其实就是对实参的操作,在引⽤传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
下⾯的代码对此作出了细致解释(从实参,形参在内存中存放地址的⾓度 说明了问题的本质,容易理解 )
1 #include<iostream>
2 using namespace std;
3 //值传递
4 void change1(int n){
5 cout<<"值传递--函数操作地址"<<&n<<endl; //显⽰的是拷贝的地址⽽不是源地址
6 n++;
7 }
8
9 //引⽤传递
10 void change2(int & n){
11 cout<<"引⽤传递--函数操作地址"<<&n<<endl;
12 n++;
13 }
14 //指针传递
15 void change3(int *n){
16 cout<<"指针传递--函数操作地址 "<<n<<endl;
17 *n=*n+1;
18 }
19 int main(){
20 int n=10;
21 cout<<"实参的地址"<<&n<<endl;
22 change1(n);
23 cout<<"after change1() n="<<n<<endl;
24 change2(n);
25 cout<<"after change2() n="<<n<<endl;
26 change3(&n);
27 cout<<"after change3() n="<<n<<endl;
28 return true;
29 }
运⾏结果如下,(不同的机器可能会有所差别)
可以看出,实参的地址为0x22ff44
采⽤值传递的时候,函数操作的地址是0x22ff20并不是实参本⾝,所以对它进⾏操作并不能改变实参的值
再看引⽤传递,操作地址就是实参地址 ,只是相当于实参的⼀个别名,对它的操作就是对实参的操作
接下来是指针传递,也可发现操作地址是实参地址
那么,引⽤传递和指针传递有什么区别吗?
引⽤的规则:
引⽤被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
不能有NULL引⽤,引⽤必须与合法的存储单元关联(指针则可以是NULL)。
⼀旦引⽤被初始化,就不能改变引⽤的关系(指针则可以随时改变所指的对象)。
指针传递的实质:
指针传递参数本质上是值传递的⽅式,它所传递的是⼀个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从⽽成为了实参的⼀个副本。值传递的特点是被调函数对形式参数的
任何操作都是作为局部变量进⾏,不会影响主调函数的实参变量的值。(这⾥是在说实参指针本⾝的地址值不会变)如果理解不了⼤可跳过这段
指针传递和引⽤传递⼀般适⽤于:
函数内部修改参数并且希望改动影响调⽤者。对⽐指针/引⽤传递可以将改变由形参“传给”实参(实际上就是直接在实参的内存上修改,不像值传递将实参的值拷贝到另外的内存地址中才修改)。
另外⼀种⽤法是:当⼀个函数实际需要返回多个值,⽽只能显式返回⼀个值时,可以将另外需要返回的变量以指针/引⽤传递给函数,这样在函数内部修改并且返回后,调⽤者可以拿到被修改过后的变量,也相当于⼀个隐式的返回值传递吧。
以下是我觉得关于指针和引⽤写得很不错的⽂章,⼤家可参照看⼀下,原⽂出处地址:
从概念上讲。指针从本质上讲就是存放变量地址的⼀个变量,在逻辑上是独⽴的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。
⽽引⽤是⼀个别名,它在逻辑上不是独⽴的,它的存在具有依附性,所以引⽤必须在⼀开始就被初始化,⽽且其引⽤的对象在其整个⽣命周期中是不能被改变的(⾃始⾄终只能依附于同⼀个变量)。
在C++中,指针和引⽤经常⽤于函数的参数传递,然⽽,指针传递参数和引⽤传递参数是有本质上的不同的:
指针传递参数本质上是值传递的⽅式,它所传递的是⼀个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从⽽成为了实参的⼀个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进⾏,不会影响主调函数的实参变量的值。(这⾥是在说实参指针本⾝的地址值不会变)
⽽在引⽤传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
引⽤传递和指针传递是不同的,虽然它们都是在被调函数栈空间上的⼀个局部变量,但是任何对于引⽤参数的处理都会通过⼀个间接寻址的⽅式操作到主调函数中的相关变量。⽽对于指针传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。如果想通过指针参数传递来改
变主调函数中的相关变量,那就得使⽤指向指针的指针,或者指针引⽤。
为了进⼀步加深⼤家对指针和引⽤的区别,下⾯我从编译的⾓度来阐述它们之间的区别:
程序在编译时分别将指针和引⽤添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,⽽引⽤在符号表上对应的地址值为引⽤对象的地址值。符号表⽣成后就不会再改,因此指针可以改变其指向的对象(指针变量中的值可以改),⽽引⽤对象则不能修改。
最后,总结⼀下指针和引⽤的相同点和不同点:
1)相同点:
都是地址的概念;
指针指向⼀块内存,它的内容是所指内存的地址;⽽引⽤则是某块内存的别名。
2)不同点:
指针是⼀个实体,⽽引⽤仅是个别名;
引⽤只能在定义时被初始化⼀次,之后不可变;指针可变;引⽤“从⼀⽽终”,指针可以“见异思迁”;
引⽤没有const,指针有const,const的指针不可变;(具体指没有int& const a这种形式,⽽const int& a是有 的, 前者指引⽤本⾝即别名不可以改变,这是当然的,所以不需要这种形式,后者指引⽤所指的值不可以改变)
引⽤不能为空,指针可以为空;
“sizeof 引⽤”得到的是所指向的变量(对象)的⼤⼩,⽽“sizeof 指针”得到的是指针本⾝的⼤⼩;
指针和引⽤的⾃增(++)运算意义不⼀样;
引⽤是类型安全的,⽽指针不是 (引⽤⽐指针多了类型检查)
⼀、引⽤的概念
引⽤引⼊了对象的⼀个同义词。定义引⽤的表⽰⽅法与定义指针相似,只是⽤&代替了*。
例如: Point pt1(10,10);
Point &pt2=pt1; 定义了pt2为pt1的引⽤。通过这样的定义,pt1和pt2表⽰同⼀对象。
需要特别强调的是引⽤并不产⽣对象的副本,仅仅是对象的同义词。因此,当下⾯的语句执⾏后:
pt1.offset(2,2);
pt1和pt2都具有(12,12)的值。
引⽤必须在定义时马上被初始化,因为它必须是某个东西的同义词。你不能先定义⼀个引⽤后才
初始化它。例如下⾯语句是⾮法的:
Point &pt3;
pt3=pt1;
那么既然引⽤只是某个东西的同义词,它有什么⽤途呢?
下⾯讨论引⽤的两个主要⽤途:作为函数参数以及从函数中返回左值。
⼆、引⽤参数
1、传递可变参数
const的作用传统的c中,函数在调⽤时参数是通过值来传递的,这就是说函数的参数不具备返回值的能⼒。
所以在传统的c中,如果需要函数的参数具有返回值的能⼒,往往是通过指针来实现的。⽐如,实现
两整数变量值交换的c程序如下:
⼀、引⽤的概念
引⽤引⼊了对象的⼀个同义词。定义引⽤的表⽰⽅法与定义指针相似,只是⽤&代替了*。
例如: Point pt1(10,10);
Point &pt2=pt1; 定义了pt2为pt1的引⽤。通过这样的定义,pt1和pt2表⽰同⼀对象。
需要特别强调的是引⽤并不产⽣对象的副本,仅仅是对象的同义词。因此,当下⾯的语句执⾏后:
pt1.offset(2,2);
pt1和pt2都具有(12,12)的值。
引⽤必须在定义时马上被初始化,因为它必须是某个东西的同义词。你不能先定义⼀个引⽤后才
初始化它。例如下⾯语句是⾮法的:
Point &pt3;
pt3=pt1;
那么既然引⽤只是某个东西的同义词,它有什么⽤途呢?
下⾯讨论引⽤的两个主要⽤途:作为函数参数以及从函数中返回左值。
⼆、引⽤参数
1、传递可变参数
传统的c中,函数在调⽤时参数是通过值来传递的,这就是说函数的参数不具备返回值的能⼒。
所以在传统的c中,如果需要函数的参数具有返回值的能⼒,往往是通过指针来实现的。⽐如,实现
两整数变量值交换的c程序如下:
void swapint(int *a,int *b)
{
int temp;
temp=*a;
a=*b;
*b=temp;
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论