C++中的参数传递方式:传值、传地址、传引用总结

技术交流QQ群:1027579432,欢迎你的加入!

  • 指针:指针是一个变量,只不过这个变量中存储的是一个地址,指向内存中的一个单元。
  • 引用:引用和原变量是同一个东西,只不过是原变量的一个别名。
        int a = 10;  定义一个整型变量a
        int *p = &a;  定义一个指向整型变量的指针变量p,该指针指向a的存储单元,即p的值是a存储单元的地址
        int &b = a;   定义一个整型变量a的引用,a和b是同一个东西,在内存中占用同一个存储单元
    

一、引用的特性:

  • 引用在定义时必须初始化
  • 一个变量可以有多个引用
  • 引用一旦绑定某个实体,就不能再是其他变量的引用。

二、引用和指针的区别与联系:

  • 1.相同点:
    • 底层的实现方式相同,都是按照指针的方式实现的
  • 2.不同点:
    • 引用定义的时候必须初始化,指针可以不用初始化;
    • 引用一旦初始化为指向一个对象,就不能再指向其他对象,而指针可以在任何时候指向任何一个同类型的对象;
    • 没有空引用,但是有空指针;
    • 在sizeof中的含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节的个数(在32为平台下,指针求sizeof永远是4);
    • 引用++改变的是变量的内容,指针++改变的是指针的指向;
    • 有多级指针,没有多级引用;
    • 引用使用起来比指针安全;
    • 如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄漏;

三、传值、传地址、传引用的区别,哪个更高效?

  • 1.传值
    • 这种传递方式中,实参和形参是两个不同的地址空间,参数传递的实质是将原函数中变量的值,复制到被调用函数形参所在的存储空间中,这个形参的地址空间在函数执行完毕后,会被回收掉。整个被调用函数对形参的操作,只影响形参对应的地址空间,不影响原函数中变量的值,因为这两个不是同一个存储空间。
      即使形参的值在函数中发生了变化,实参的值也完全不会受到影响,仍为调用前的值。
  • 2.传地址
    • 这种传递方式中,实参是变量的地址,形参是指针类型的变量,在函数中对指针变量的操作,就是对实参(变量地址)所对应的变量的操作,函数调用结束后,原函数中的变量的值将会发生改变。
      被调用函数中对形参指针所指向的地址中内容的任何改变都会影响到实参。
  • 3.传引用
    • 这种传递方式中,形参是引用类型变量,其实就是实参的一个别名,在被调用函数中,对引用变量的所有操作等价于对实参的操作。这样,整个函数执行完毕后,原先的实参的值将会发生改变。
      被调函数对形参做的任何操作都影响了主调函数中的实参变量。
  • 4.哪种更高效?
    • 在内置类型当中三种传递方式的效率上都差不多;
    • 在自定义类型当中,传引用方式效率的更高效一些,因为它没有对形参进行一次拷贝

四、常引用

int a = 10;
int &b = a;  等价于 int *const b = a;即引用是一个指针常量(又称常指针,即一个常量,其类型是指针)
常引用:const int &a=b;等价于const int * const a=b;不仅仅是a这个地址不可修改,而且其指向的内存空间也不可修改。

五、引用的使用场景

  • 1.给变量起别名
    int a;
    int &b = a;
    
  • 2.将引用作为函数的参数
    • 使用引用类型就不必在swap中声明形参是指针变量,指针变量要另外开辟内存单元,其内容是地址。而引用变量不是一个独立的变量,不单独占内存单元。而且在调用swap函数时,只需要传值即可,将引用作为函数的形参更加简单、直观、方便。
      void swap(int &a, int &b)
      {
          int temp;                               
          temp = a;                                
          a = b;                                   
          b = temp;
      }
      
  • 3.返回值
        // 值返回
        int Add(int _iLeft, int _iRight)
        {
        return _iLeft + _iRight;
        } 
        // 引用返回
        int& Add(int & _iLeft, int& _iRight)
        {
        int iResult = _iLeft + _iRight;
        return iResult;
        }
    

六、代码实例

#include "iostream"

using namespace std;

void swap_by_value(int x, int y);
void swap_by_pointer(int *p1, int *p2);
void swap_by_reference(int &x, int &y);
/*
C++的函数参数传递方式,可以是传值方式,也可以是传引用方式。
传值的本质是:形参是实参的一份复制。
传引用的本质是:形参和实参是同一个东西。
传值和传引用,对大多数常见类型都是适用的。指针、数组,它们都是数据类型的一种,没啥特殊的
因此,指针作为函数参数传递时,也区分为传值和传引用两种方式。
void fun_1(int a); int类型,传值(复制产生新的变量)
void fun_2(int &a); int类型,传引用(形参和实参是同一个东西)
void fun_3(int *pi); 指针类型,传值(复制产生新的变量)
void fun_4(int *&pi); 指针类型,传引用(形参和实参是同一个东西)
如果希望通过将参数传递到函数中,从而来改变变量的值(比如变量是T a,T表示类型),
则可以有这2种方式选择:
    1.传a的引用: void my_fun(T &a);
    2.传a的地址: void my_fun(T *a);
*/

// 一、值传递
/*
形参意思是被调用函数的参数/变量,实参意思是主调函数中放到括号中的参数/变量。
传值方式下,形参是实参的拷贝:重新建立了变量,变量取值和实参一样。
即实参a和b的值为20和10,形参x和y的值都是20和10;而a与x的地址、b与y的地址并不相同
表明形参x和y是新建的变量,也即实参a, b是从形参复制了一份
*/

int main(){
    int a = 20, b = 10;
    cout << "交换前: " << endl; 
    cout << "a = " << a << " ,b = " << b << endl;
    swap_by_value(a, b); // 值传递
    cout << "交换后: " << endl;
    cout << "a = " << a << " ,b = " << b << endl;
    /*-------------------------分界线1------------------*/
    cout << "交换前: " << endl; 
    cout << "a = " << a << " ,b = " << b << endl;
    swap_by_pointer(&a, &b); // 值传递
    cout << "交换后: " << endl;
    cout << "a = " << a << " ,b = " << b << endl;
    /*-------------------------分界线2------------------*/
    cout << "交换前: " << endl; 
    cout << "a = " << a << " ,b = " << b << endl;
    swap_by_reference(a, b); // 引用传递
    cout << "交换后: " << endl;
    cout << "a = " << a << " ,b = " << b << endl;
    return 0;
}


void swap_by_value(int x, int y){
    int temp;
    temp = x;
    x = y;
    y = temp;
}

// 二、传指针(地址),实质还是传值
void swap_by_pointer(int *p1, int *p2){
    int t;
    t = *p1;
    *p1 = *p2;
    *p2 = t;
}

// 三、传引用
/*
传引用,传递的是实参本身,而不是实参的一个拷贝,形参的修改就是实参的修改,即值相同,地址也相同
相比于传值,传引用的好处是省去了复制,节约了空间和时间。
假如不希望修改变量的值,那么请选择传值而不是传引用。
*/

void swap_by_reference(int &x, int &y){
    int temp;
    temp = x;
    x = y;
    y = temp;
}

// 四、总结
/*
“引用”类型变量的声明方式:变量类型 &变量名;   int &b;
“指针”类型的声明方式:基类型 *变量名;  int *pi;
“指针的引用类型”应当这样声明:基类型 *&变量名   int *&pi;
指针类型,也是有传值、传引用两种函数传参方式的:
    1.指针的传值方式
        void my_fun(int *a, int n);
    2.指针的传引用方式
        void my_fun(int *&pi, int n);
// 普通类型,以int a为例
void myfun(int a)    //传值,产生复制
void myfun(int &a)   //传引用,不产生复制
void myfun(int *a)   //传地址,产生复制,本质上是一种传值,这个值是地址
// 指针类型,以int *a为例
void myfun(int *a)   //传值,产生复制
void myfun(int *&a)  //传引用,不产生复制
void myfun(int **a)   //传地址,产生复制,本质上是一种传值,这个值是指针的地址
// 数组类型,以int a[10]为例
void myfun(int a[], int n) //传值,产生复制
void myfun(int* a, int n) //传值,产生复制,传递的数组首地址
void myfun(int (&arr)[10]) //传引用,不产生复制。需要硬编码数组长度
template<size_t size> void myfun(int (&arr)[size]) //传引用,不产生复制。不需要硬编码数组长度
*/

七、参考博客

1.Chris的技术博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容

  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 3,430评论 3 44
  • 1.语言中变量的实质 要理解C指针,我认为一定要理解C中“变量”的存储实质, 所以我就从“变量”这个东西开始讲起吧...
    金巴多阅读 1,745评论 0 9
  • 第十章 指针 1. 地址指针的基本概念: 在计算机中,所有的数据都是存放在存储器中的。一般把存储器中的一个字节称为...
    坚持到底v2阅读 1,066评论 2 3
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,513评论 1 51
  • __block和__weak修饰符的区别其实是挺明显的:1.__block不管是ARC还是MRC模式下都可以使用,...
    LZM轮回阅读 3,293评论 0 6