函数的调用方法

0.概述

如果你能很清楚的理解下面这个程序,那么本篇文章就不用看了。班门弄斧而已。

void GetMemory(char* p){
	p = new char[100];
}

void main(){
	char *str = "1234";
	GetMemory(str);
	strcpy(str, "hi"); //出错! str = NULL!
	printf("%s\n", str);
}

看不懂的话,可以好好学习一下。对于初学者来说,C/C++的参数传递的确让人感觉非常困惑。“传地址”,“传值”,“传引用”,“传指针”等等,功能又似乎很相似,实在难以区分。本文就C/C++参数传递问题做以总结。

一.错误认识:

很多人把C/C++中函数的调用方式分为两种:一种是传值的,一种传地址的。传值比较常见,形参变实参不变。 而传地址形参实参都变,而且有两种实现方式:指针和引用。除了大众意义上的指针和引用的区别(一个可变一个不可变)外,其他视为一样。

如果你也有这上面的错误认识,那你可要认真看下面的文章了。上面这段程序,主函数中调用GetMemory(str),只是把一个str指向的地址传递到子函数的参数p,即p指向str所指向的地址,你可以修改p指向值的内容,但你不可以让p指向别的地方。比如你让p指向了一个新的char数组。那么主函数中的实参str指向的那个地址的值是不会改变的。如图所示:


如果你写成以下这样就可以了:

void GetMemory(char* &p){
	p = new char[100];
}

void main(){
	char *str;
	GetMemory(str);
	strcpy(str, "hi"); //OK!
	printf("%s\n", str);
}

因为p是主函数str的一个引用,可以说就是str,所以你改变p的指向,也就是改变了str的指向。

所以我之前最大的错误就是对调用方式头脑中的分类不正确。我所谓的指针传地址仍旧属于传值调用。

二:总结

1:类型

变量里面存取的东西称为值,我们可以存普通数值123,也可以123所在内存中的地址;可以存对象A,也可以存对象A在内存中的地址。所以值有两种:普通值和地址值。用于存取普通值的变量就是我们经常说的变量,用于存取地址值的变量就是指针变量(地址值变量)。

C语言:有普通值变量,也有指针变量。

C++语言:有普通值变量,指针变量,还有引用。所谓引用就是给一个变量起个别名。同理,可以给普通值变量起别名,也可以给指针变量起别名。

JAVA语言:只有基本类型可以显示的使用普通值变量,对象只能用引用(JAVA里面的引用是指针变量)来使用。

例如:

C++:Mytype A;定义了一个Mytype对象,A就是一个普通变量。

Java:Mytype A;定义了一个指向Mytype对象的引用(指针)。

注意JAVA和C++的不同。

2:只有两种调用方式:
(1): 传值调用:分为传值和传地址。注意这句话中第一个“值”有两种意思。
(2): 引用调用:同理,你引用的可以是值,可以是地址,可以使指针。

下面再检验一下是否过关吧!看下面代码:

void Change(char *p)
{
    *p = 'b';
    p = NULL;
}
main()
{

    char a = 'a';
    char* p = &a;
    Change(p);
    printf("%c\n", a);           //值a改变!

}

三:代码

/*
 * 对于所有的参数传递你都可以理解为把实参赋给形参。代码中将以此理念讲解。
 * 首先C++的参数传递包括两类:传"值"和传"引用"。C语言只有传值,因为C语言没有引用这个东西。
 * 值有两种:A.普通的值 B.地址
 * 引用也有两种:A.引用一个普通值 B.引用一个指针
 * 注意区分以下测试
 */

//测试1:传普通值
void fun1(int value){
    value = 10;
}

//测试2:传地址值(俗称指针传递方式,指针不就是一个地址么,指针变量不就是一个可变地址么)
void fun2(int* value){
    *value = 10;
}

//测试3:传地址值
void fun3(int* value){
    value = new int(10);
}

//测试4: 传地址值
void fun4(int* value){
    int a = 10;
    value = &a;
}

//测试5: 传引用
void fun5(int &value){
    value = 10;
}

//测试6: 传引用
void fun6(int* &value){
    value = new int(10);
}

int main(){
    //测试1调用
    int v1 = 2;
    fun1(v1); //相当于int value(形参:基本类型) = v(实参:基本类型);换句话说把数字2交给函数fun的形参,让fun处理一些工作。当然不会造成value的改变了。
    cout << v1 << endl;

    //测试2调用
    int v2 = 2;
    fun2(&v2); //相当于int* value(形参:指针类型) = &v2(实参:一个地址); 
    cout << v2 << endl;	//实参把他的地址交给形参value后,此时value为实参v2的地址。那么对*value(value指向的内容)的修改,当然会导致实参的改变。


    //测试3调用
    int v3 = 2;
    fun3(&v3); //相当于int* value(形参:指针类型) = &v3(实参:一个地址)
    cout << v3 << endl;	//此时value为实参v3的地址。可是函数fun3又让value指向了一块新开辟的地方(value = new int(10)); 那么对这块新开辟地方的修改                                  //当然不会影响到实参了。


    //测试4调用
    int v4 = 2;
    fun4(&v4); //同测试3
    cout << v4 << endl;

    //测试5调用
    int v5 = 2;
    fun5(v5); //相当于int &value(形参:引用类型) = v5(实参:基本类型); 一个引用一旦被赋值就完全代表了他引用的变量。
    cout << v5 << endl;	//value此时就成了v5的另一个名字。对value的任何修改都会引起value的改变。


    //测试6调用
    int a = 2;
    int *v6 = &a;
    fun6(v6); //这个有难度啊。是引用调用,但是是一个指针类型的引用。上面是基本类型的引用。理解理解。
    cout << *v6 << endl;

    return 0;
}

四. 疑惑

指针的用法非常灵活,引用其实就是一个常量指针,如int &p其实就是int* const p,一旦确定指向就不能再指向别的了。那么完全可以不需要引用这种方式吗?这就要从历史的角度理解了。指针是C语言的一个十分重要的概念,而C++语言是C语言的的超集,当然可以使用指针了。而C++语言虽然可以使用指针,但是它并没有像C语言中那样重要,因为在C++语言中又引进了引用的概念,引用占据了指针的一些作用,而使指针的功能相对地减弱了。
引用这个概念看起来更加简单,使用起来不像指针那么难,容易出错。因此这个概念变得重要且被广泛使用。到Java语言的时,引用被使用进来,可是Java中的引用变得更加灵活了,灵活的又像当年的指针一样,可以指来指去。而且作为Java语言传参的主体。如果希望只指向一次,那就加上const就行了。
这应该就是参数传递的发展渊源,似乎又绕回去了,不过使用起来更加简化了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值