• 指针,引用和传参——程序员的自我修养


    指针是C语言中就有的,引用在Cpp中才有的特性。


    一,指针
    int* a 这个语句的含义是分配空间。
    1,分配多大的空间?
    指针就是为了保存地址的,所以分配的空间大小是一个word(以32bitCPU为例,那么就是4个Byte)。
    不论指针指向的类型,指针都是保存的一个地址,double* a, Struct* a都是一个地址。

    2,指针所指的位置只是一个字节,那么指针怎么样保存类型信息?
    指针不保存类型信息,所谓的类型信息只是为了帮助程序员记忆和约束程序员不要乱用而已。源代码编译出的可执行文件中并不需要知道指针所指数据的类型。

    3,那么假如有结构体,

    struct Struct{
    int df1,
    double df2
    }
    Struct* a;
    double b = a->df2;


    最后一句运行时,只知道a是堆上某个对象的首字节的地址,又不知道a所指对象的类型,怎么知道a.df2是什么呢?
    因为访问df2是通过首字节地址和偏移量来访问的,因为在编译时Struct的结构是明确的,其中的所有字段类型和顺序是已知的,那么访问df1和df2时候就知道他们距离首字节的偏移是多少。之所以使用->df2这样的语法主要是C语言提供的feature,就是为了用更加接近人可以阅读的方式表达程序逻辑——这是所有高级编程语言追求的目标。


    4,运行时不保存指针类型信息,为什么C语言提供多种指针类型?
    首先编译器需要啊,编译a->df1这个token时,编译器会检查a所指的类型Struct的字段df1,因为需要根据df1类型计算偏移量啊。还有一个好处是能够约束程序员,减少编程出错啊,想想Java5之前对于容器类并没有泛型,照样可以写出同样功能的代码。

    二,引用
    引用类型,看两个语句
    int a=9 ;//分配空间,大小为int类型所需大小,这个内存空间有个名字叫a,
    int& aa=a;//不分配空间,名字为a的空间还有一个名字叫aa

    5、为啥要有引用,a代表的内存空间已经有名字了,为啥还要取名字aa,不够用吗?
    嗯,其实不要这个引用的特性,照样编程,C语言就没有啊。如果在同一个函数中,取aa这个别名几乎没有意义。意义在于函数传参的时候。

    函数的形式参数,实际上完全可以看成函数内部的局部变量,函数接受调用者传递来的参数,相当于总是在函数体开始执行前,执行对形参的赋值语句。例如,

    void out(){
    int a=1;
    int b=2;
    swap(a,b);
    }


    有两种swap

    void swap(int x, int y){//传值
    int tmp = x;
    x = y;
    y = tmp;
    }
    void swap(int& x, int& y){//传引用
    int tmp = x;
    x = y;
    y = tmp;
    }


    首先,out函数作为swap的调用者,会把a,b两个值传递给swap,这个过程就相当于函数体最前面多了赋值语句,黑体部分只是想象出来的,swap函数声明的内部根本没有叫做ab的变量,这不是合法的C语言,但是所有函数调用传参过程都可以这么理解,其中ab是通过函数调用之间压栈得到的:
    void swap{//传值
    int x=a;
    int y=b;
    int tmp = x;
    x = y;
    y = tmp;
    }
    void swap{//传引用
    int& x=a;
    int& y=b;
    int tmp = x;
    x = y;
    y = tmp;
    }
    int x=a;和int& x=a;的区别就是前者分配新的内存,后者不分配新内存,那么传值的swap调用后,out函数中的ab取值不会改变,因为swap内部的xy会先分配内存,然后复制ab的值,然后分别改变xy空间上的值,对外面名字叫做ab的空间毫无影响;
    而传引用的swap,xy只是out中ab变量的别名,分别修改xy代表的空间中的值,意味着名字叫做ab的空间存储的值也改变了。
    有的时候,程序员需要传值,有的时候需要传引用,Cpp提供了这样一个更多的选择,不要骂Cpp了。

    6,C语言中没有引用的概念,被调函数怎么修改调用者传递进来的参数?
    通过指针。

    void swap(int* x, int* y){//指针作为参数
    int tmp = *x; //取出x指向的空间的值,也就是名字为a的内存空间里的值
    *x = *y;
    *y = tmp;
    }
    //当然调用时也做相应配合,取&a, &b做参数
    void out(){
    int a=1;
    int b=2;
    swap(&a, &b);
    }


    前面已经说过,函数调用传参相当于声明局部变量并且调用赋值语句,
    void swap(int* x, int* y){//指针作为参数
    int* x = &a;
    int* y = &b;
    int tmp = *x; //取出x指向的空间的值,也就是名字为a的内存空间里的值
    *x = *y;
    *y = tmp;
    }
    指针就是一个32bit的无符号整数,
    int* x = &a;
    首先需要分配指针x的空间,然后把指针a的地址赋给x,此时x和a都有一个32bit的空间,存储的值相等,都是指向同一个地址,这个地址的空间上存储着1。

    所以没有引用,用指针也能实现相关的功能,Cpp的引用只是给程序员提供了更多的选择而已。

    三、函数调用的传参和返回
    TODO

  • 相关阅读:
    Spark 内核架构+宽依赖与窄依赖+基于Yarn的两种提交模式
    Spark RDD高级编程:基于排序机制的wordcount程序+二次排序+topn
    Spark RDD持久化原理+共享变量原理(Broadcast Variable和Accumulator)
    Spark RDD工作原理详解+RDD JAVA API编程
    Spark 程序设计详解
    剑指offer 39.知识迁移能力 平衡二叉树
    剑指offer 38.知识迁移能力 二叉树的深度
    ElasticSearch 倒排索引原理+document写入流程+数据恢复
    剑指offer 37.知识迁移能力 数字在排序数组中出现的次数
    剑指offer 36.时间空间效率的平衡 两个链表的第一个公共结点
  • 原文地址:https://www.cnblogs.com/linlei2099/p/8810771.html
Copyright © 2020-2023  润新知