深入理解指针和引用

发布于 2020-04-12  117 次阅读


指针和引用的总结

  1. 引用必须初始化,指针不用
  2. 引用的赋值是改变关联对象的值,指针的赋值是改变关联的对象
  3. 引用就是关联对象,只是换了个马甲!!!

定义方式

int m = 1;
int &q = m;//引用的定义
int *p = &m; //指针的定义

取值的方式

printf("m:%d,q:%d,*p:%d\n",m,q,*p);

查看地址的方式

printf("%p\n",&m);
printf("%p\n",&q);
printf("%p\n",p);

// 000000000061FE0C
// 000000000061FE0C
// 000000000061FE0C

三者地址相同
引用和其关联对象的使用方式一模一样,我们可以说引用就是关联对象,下面我们看看实例

m=2;
printf("m:%d,q:%d,*p:%d\n",m,q,*p);

q=3;
printf("m:%d,q:%d,*p:%d\n",m,q,*p);

*p=4;
printf("m:%d,q:%d,*p:%d\n",m,q,*p);

// m:2,q:2,*p:2
// m:3,q:3,*p:3
// m:4,q:4,*p:4

引用的所有操作和关联对象的操作一致,指针要用*来获取地址指向的值

引用和指针的赋值

int n = 100;
q = n;
p = &n;
printf("m: %p, n: %p\n",&m,&n);
printf("q: %p, p: %p\n",&q,p);

printf("m:%d, n:%d, q:%d, *p:%d\n",m,n,q,*p);
// m: 000000000061FE0C, n: 000000000061FE08
// q: 000000000061FE0C, p: 000000000061FE08
// m:100, n:100, q:100, *p:100

引用的地址依然是初始时的地址,而指针的地址已经指向了赋值后的地址,所以,引用的赋值是对关联对象值的修改,指针的赋值是修改关联对象

值传递和引用传递

值传递

传递的是变量的值(形参),通常我们称变量自身为实参,函数拷贝的是形参,形参是实参的拷贝,函数对形参的修改,不影响实参,形参在函数结束后会销毁,指针传递也是值传递,只是这个值是地址的值。

举一个经典的例子,交换a和b的值

#include<stdio.h>
void swap1(int a,int b){
    int tmp = a;
    a = b;
    b = tmp;
}
void swap2(int *a,int *b){
    int tmp = *a;
    *a = *b;
    *b = tmp;
}
main(){
    int a = 10;
    int b = 100;

    swap1(a,b);
    printf("%d %d\n",a,b);

    int *p1=&a;
    int *p2=&b;

    swap2(p1,p2);//swap2(&a,&b);
    printf("%d %d\n",a,b);
}
// 10 100
// 100 10

第一种失败了,因为函数修改的是形参,不影响实参;第二种成功了,它传递的是变量的地址,修改的是地址指向的值

引用传递

引用传递,传递的就是变量本身(实参),不需要进行拷贝,在函数内修改变量和外面修改变量效果一致

#include<stdio.h>
void swap(int &a,int &b){
    int tmp = a;
    a = b;
    b = tmp;
}
main(){
    int a = 10;
    int b = 100;

    swap(a,b);
    printf("%d %d\n",a,b);
}
// 100 10

引用传递修改的就是变量自身

指针和引用指针

指针传递的本质
#include<cstdio>
void change1(int *p){
    int c=2;
    p=&c;
}
void change2(int *p){
    *p=2;
}
main(){

    int m = 10;
    int *c = &m;

    change1(c);
    printf("%d ",*c);
    change2(c);
    printf("%d\n",*c);
}
//10 2

结论:只能修改指针指向的值,不能修改指针本身;指针传递的本质是值传递,值是地址的值。

修改指针本身
#include<cstdio>
void  change3(int **p){
    int c=90;
    *p = &c;
}
main(){

    int m = 10;
    int *c = &m;
    change3(&c);
    printf("%d\n",*c);
}
//90

也可以用引用指针达到相同目的

#include<cstdio>
void change4(int *&p){
    int c=1000;
    p=&c;
}
main(){

    int m = 10;
    int *c = &m;

    change4(c);
    printf("%d\n",*c);
}
//1000

*&p代表这是一个引用指针,和上面交换a,b中&a,类似,上面是可以直接修改a,b的值,这里是可以直接修改p的值

参数调用表格
参数 调用
func(int * n) func(&n) or func(p)
func(int **n) func(&p)
func(int &n) func(n)
func(int *&n) func(p)

int *p;int n;

指针传递和引用传递的异同
  • 都是传地址,只是指针是传递地址的值,而引用相当于直接传递真实的地址,可以说它就是那块地址的名字
    1. 引用++相当于变量++,指针++,是地址往后挪一个int长度
    2. 引用是类型安全的,引用过程会进行类型检查;指针不会进行安全检查;
    3. sizeof(引用)是变量的大小,sizeof(指针)是指针的大小

现在可以理解链表中不同参数的意思了

LNode *L,LinkList L,和LinkList &L
  • LinkList L 等价于 LNode *L
  • LinkList &L 等价于 LNode *&L

*以LNode L为参数**

#include<stdio.h>
#include<malloc.h>
typedef struct LNode{
    int val;
    struct LNode *next;
}LNode,*LinkList;
void CreateList(LNode *header){//等价于 LinkList header
    header=(LNode*)malloc(sizeof(LNode));
    header->val=100;
    header->next=NULL;
}
int main(){
    LinkList head=NULL;
    CreateList(head);
    if(head!=NULL){
        printf("%d\n",head->val);//什么都没有输出 
    }
    free(head);
    return 0;
}

函数中修改的是指针的值,这里用的值传递,并不能改变指针本身,所以失败了
*以LNode &L为参数**

#include<stdio.h>
#include<malloc.h>
typedef struct LNode{
    int val;
    struct LNode *next;
}LNode,*LinkList;
void CreateList(LNode *&header){//等价于LinkList &L
    header=(LNode*)malloc(sizeof(LNode));
    header->val=100;
    header->next=NULL;
}
int main(){
    LinkList head=NULL;
    CreateList(head);
    if(head!=NULL){
        printf("%d\n",head->val);//100 
    }
    free(head);
    return 0;
}

这里传递的是指针的引用,函数对指针的修改有效,所以成功了


天空没有鸟的痕迹,但我已飞过。