函数何时值传递,何时指针,何时引用传递总结

来源:互联网 发布:php 执行shell脚本 编辑:程序博客网 时间:2024/06/05 15:11

编程中定义函数,一般三种传递方法,看是简单,想灵活合理选择,还需要大量的编程经验和技巧。 故在这里特意总结一下这三种传递的方法。

根本区别: 函数值传递不改变变量值,如果想改变变量值,需要返回值,然后用变量接收一下这个返回值。  而指针传递和引用传递都可以在函数内改变变量值,不需要通过返回值的形式改变。

应用场合: 当想通过这个函数,改变好几个变量的值,多个变量都通过函数返回值来改变变量值方式费时费力。所以这种场合就比较适合使用指针和引用。

指针传递需要开内存,如果忘记释放的话,可能导致内存泄露。浪费大量内存。所以引用传递更好一些。

引用传递的性质象指针传递,书写形式象值传递,

理由:如果只需要借用一下别名,就没必要用指针,.

void changeVar(int &myVar,int newValue);  // 引用传递

这个函数第一个输入的参数不是一个指针,它是那个将被传递给这个函数的原始变量的一个别名。在changeVar()函数里对这个参数变量进行的任何操作都将反映在changeVar()函数外的那个原始变量身上。这意味着changeVar()函数与原来的一样。

void changeVar(int myVar,int newValue){  //值传递,没有返回值,起不到改变变量的作用

  myVar=newValue;

}

这使得这个函数更容易被调用----只需要提供一个变量名;

int main(){

int myNum=20;

changeVar(myNum,90);  // 引用和值传递写法一样, 可见引用传递简单,这也是c++新增加的功能,表现了c++的强大

}

这比值传参语法上更简单了。

引用传递”方式把参数值传递给一个函数是C++的新增功能,这可以让函数的调用语法更加简单清晰。

提示:1.在定义函数时,还可以让它以引用传递方式而不是以值传递方式返回: int &myFuntion();

           2.除了可以改变有关变量的值,引用传递方式的另一个好处是它的开销相对要小一些:因为不需要在函数里创建临时变量来容纳那些值,程序的内存占用量当然会小一些。

           3.如果想获得引用传递方式带来的性能改善,但不想改变某个变量的值,可以把相应的输入参数定义为一个常量:

 void myFunc(const int &myNum);

通过这样定义的函数,可以把具体的参数直接传递给它:myFunc(7);


 
int script_orientation(VOID *dic, unsigned char *im, int w, int h,                               RECT roi, int &script_id,  // NOLINT                               int &orient_id,  // NOLINT                               float &conf) {  // NOLINT
函数调用

 
 const char* model_file = "model.txt";
 void* model = CNN_Init(model_file);
cv::Mat image = cv::imread(path, 0);
    int script_id, orient_id;

script_orientation(model, image.data, image.cols, image.rows, roi,                               script_id, orient_id, confidence);
这里需要script_id, orient_id 两个返回值, 所以这两个定义为应用较好。 调用时就不需要再去定义变量接收 这两个 script_id, orient_id 两个返回值


下面写一个函数,从函数中把我需要的两个值传出来,由于传出来的值比较多,所以不考虑用return来返回,需要通过参数把修改后的值拉出来供我使用,很当然的就想到了用指针,但是值就是传不出来;使我对原有的大脑中指针的思维产生混沌感,今天一上午才把函数传递又走了一遍,才明白其中道理(道行还是很浅),现在整理如下:

    我之前写的方法部分代码,相信好多人都是这么写的:
    
[cpp] view plain copy
  1. BOOL GetStartEndBoxes(BOOL bRow, const SwCrsrShell& rShell,  SwTableBox *pStt,  SwTableBox *pEnd)  
  2. {  
  3.     SwSelBoxes aBoxes;  
  4.     ... ...  
  5.   
  6.     pStt = aBoxes[0];  
  7.     pEnd = aBoxes[aBoxes.Count() - 1];  
  8.     return TRUE;  
  9. }  
  10. 调用:  
  11.        。。。 。。。      SwTableBox *pStt=0;  
  12.       SwTableBox *pEnd=0;  
  13.   
  14.      if ( !GetStartEndBoxes(bRow, *this, pStt, pEnd) )  
  15.     return FALSE;  

    传递一个指针到调用函数,希望获得更新后的值(pStt, pEnd),为什么没有获得到呢?疑问

    概念

   首先从概念上来说一下这几种函数传参方式及区别:
   1、值传递:形参是实参的拷贝,改变函数形参的值并不会影响外部实参的值,这是最常用的一种传参方法,也是最简单的一种传参方法,只需要传递参数,返回值那是return考虑的;
   2、指针传递:指针传递参数从本质上来说也是值传递,它传递的是一个地址。【值传递过程中,被调函数的形参作为被调函数的局部变量来处理,即在函数内的栈中开辟内存空间以存放由主调函数放进来的实参的值,从而成了实参的一个副本(记住这个,函数内参数的是实参的副本)】。由于指针传递的是外部实参的地址,当被调函数的形参值发生改变时,自然外部实参值也发生改变。
   3、引用传递:被调函数的形参虽然也作为局部变量在栈中开辟了内存空间,但是栈中存放的是由主调函数放进的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中实参变量(实参和形参通过引用,合二为一,说白点就是:一个人,有两个名字那种;后面想会详细说)。因此,形参的任何改动都会直接影响到实参。

   实例    

先从简单的例子开始:
  1.  值传递:例子略过。  
  2. 指针传递:
[cpp] view plain copy
  1. void swap(int *a,int *b)  
  2. {  
  3.     int temp;  
  4.     temp=*a;  
  5.     *a=*b;  
  6.     *b=temp;  
  7.     cout<<"a=" <<a<<" ,"<<"b="<<b<<endl;  
  8.     cout<<"*a=" <<*a<<" ,"<<"*b="<<*b<<endl;  
  9.     cout<<"&a=" <<&a<<" ,"<<"&b="<<&b<<endl;  
  10. }  
(刚上大学的时候就接触过的例子,交换值)调用:
[cpp] view plain copy
  1. int main(){  
  2.     int x=1;  
  3.     int y=2;  
  4.     cout<<"x=" <<x<<" ,"<<"y="<<y<<endl;  
  5.     cout<<"&x=" <<&x<<" ,"<<"&y="<<&y<<endl;  
  6.     swap(&x,&y);  
  7. }  
一定要记住这种调用方式
[cpp] view plain copy
  1. swap(&x,&y);  
如指针传递的概念上所述,传地址给形参。
形如:int *a = &x;//用于指针传递,a有自己独立的内存地址,存储的内容是x的地址,*a是存x的值。
输出结果:

传入值的各变量的初始状态(地址状态):
               
上图关系可以知道:a(b)是一个指向外部实参地址的指针,*a(*b)是指针的内容,如果改变了*a(*b)也必然导致外部实参的改变

交换后:
       *a=2,*b=1;
       
       
这样的结果是由于a或者b指针指向x或者y的地址的缘故,因此由于*a,*b值得交换导致外部实参发生变化。

 
        思考一下,下面的操作能否实现值得变化?
        简单测试代码:
       
[cpp] view plain copy
  1. int change(char* name){  
  2.     cout<<"*******CHANGE--BEFORE******"<<endl;  
  3.     cout<<"name=" <<name<<endl;  
  4.     cout<<"*name=" <<&name<<endl;  
  5.     name="alter";  
  6.     cout<<"*******CHANGE--AFTER********"<<endl;  
  7.     cout<<"name=" <<name<<endl;  
  8.     cout<<"*name=" <<&name<<endl;  
  9.     return 1;  
  10. }  
  11. int main()  
  12. {  
  13.     char *str = "this is a test";  
  14.   
  15.     cout<<"******MAIN--BEFORE*****"<<endl;  
  16.     cout<<"str=" <<str<<endl;  
  17.     cout<<"*str=" <<&str<<endl;  
  18.     change(str);  
  19.     cout<<"*****MAIN--AFTER*****"<<endl;  
  20.     cout<<"str=" <<str<<endl;  
  21.     cout<<"*str=" <<&str<<endl;  
  22.     return 1;  
  23. }  
    执行结果:(打印的输出的时候,有点错误,*str应该为 &str)
    从结果中发现,并未达到改变值得效果,为什么?这个测试代码和本文开始的疑问是一样的,那就进一步分析:
    
    传入值的各变量的初始状态(地址状态):
     
   执行赋值操作
[cpp] view plain copy
  1. name="alter";  
    系统首先需要给字符串“alter”分配内存空间(地址),然后指针才指向其地址。
       
 所以*str并没有发生变化,因此最后打印出来的仍是“this is a test”,这也解释了我开始时的迷惑!微笑
另一种成功传递参数的指针调用方法----指针的指针:
  
[cpp] view plain copy
  1. void my_malloc(void** p, int size)  
  2. {  
  3.     *p = malloc(sizeof(int)*size);  
  4. }  
  5. int main()  
  6. {  
  7.     int *a;  
  8.     my_malloc(&a , 10);  
  9.     return 1;  
  10. }  
执行结果:(有些参数没有用,只是为了打印出来看看)
当我们没有执行到给*p分配空间的时候:
执行malloc(size) 后的图如下
 
   赋值给*p后:由于p指向&a即a的地址,*p则指向a的地址里的值,现在又要把分配的内存指向*p,所以,a的值即为新分配的内存!(这个比较难转圈
   
    
    然后,我们就给指针a 分配内存成功了。
     3、引用传递:
     
[cpp] view plain copy
  1. void swapref(int &a,int &b)  
  2. {  
  3.     cout << "******************before swapref:******************"<<endl;  
  4.     cout<<"a=" <<a<<" ,"<<"b="<<b<<endl;  
  5.     cout<<"&a=" <<&a<<" ,"<<"&b="<<&b<<endl;  
  6.     int temp;  
  7.     temp=a;  
  8.     a=b;  
  9.     b=temp;  
  10.     cout << "******************after swapref:******************"<<endl;  
  11.     cout<<"a=" <<a<<" ,"<<"b="<<b<<endl;  
  12.     cout<<"&a=" <<&a<<" ,"<<"&b="<<&b<<endl;  
  13. }  
  14. int main(){  
  15.     int x=1;  
  16.     int y=2;  
  17.     cout<<"******MAIN--BEFORE*****"<<endl;  
  18.     cout<<"x=" <<x<<" ,"<<"y="<<y<<endl;  
  19.     cout<<"&x=" <<&x<<" ,"<<"&y="<<&y<<endl;  
  20.     //swap(&x,&y);  
  21.     swapref(x, y);  
  22.     cout<<"*****MAIN--AFTER*****"<<endl;  
  23.     cout<<"x=" <<x<<" ,"<<"y="<<y<<endl;  
  24.     cout<<"&x=" <<&x<<" ,"<<"&y="<<&y<<endl;  
  25. }  
一定要记住这种调用方式
[cpp] view plain copy
  1. swapref(x, y);  
形如:int &a=x; //用于引用传递,可以理解为a就是x,x就是a,只不过名字不一样
执行结果:
    这个具体就不分析了,记住引用传递实参和形参是一样的,只是名字不同而已。


总结:
       本文重点还是在参数传指针方面,指针确实令人头疼,今天遇到了,会出错,弄明白以后,等过段时间,又忘了,又遇到错误,再来看,这样不断反复,希望能不断的提升,对指针的认识不断的加深!

0 0
原创粉丝点击