C语言多级指针作形参,C语言------------c++中的引用(引用和多级指针的对比)

说明:C语言中是没有引用的,c++中才有引用,本人的C语言的代码的文件后缀名是  .cpp。原因是c++语言的编译器兼容C语言

如果后缀名写成.c那么会有好多不便,尤其是老版本的c语言的编译器更是用起来气人。

引用:

定义:为对象起了另外一个名字,引用类型去引用另一种类型,通过将声明符写成&d的形式来定义引用类型,其中d是声明的引用类型的变量名。&d:表示引用变量中存储的内容(即引用所指向的变量的地址);d:表示以引用变量中存储的内容为地址的变量中的值。引用变量和指针变量一样都是存的所指向变量的地址,不同点指针自身变量自身有地址,而引用变量没有地址(系统为引用变量自身分配了地址,只不过引用变量自身的地址我们连读的权限都没有,所以我们默认引用自身没有地址,即引用类似常量),所不能声明引用的引用,也不能声明指向引用的指针,但是可以声明指向指针的指针,也可以声明指向指针的引用

引用的实质:从汇编的角度来看,当使用到一个变量的引用时,编译器就把变量的地址(即引用)直接压入堆栈,函数从堆栈中取出这个地址就可以操作这个变量了。所以说引用不是变量是对的,这只是内存空间的一个地址。引用的作用机理。

但是计算机真的就没有给引用用变量分配内存空间吗?其实引用变量系统是分配了内存空间的:当引用变量有所指向的时候,他就相当于一个操作受限的指针,引用的底层使用指针常量实现了让我们访问不到引用变量本身的地址,&r:表示引用变量中存储的内容(即引用所指向的变量的地址或者说被引用的目标变量的变量地址),r表示被引用目标变量的值。

&:什么时候当做引用标识符,什么时候当做取地址符

&出现在声明中时&当做是引用声明符,其他情况当做取地址符例如 i = 10;  int &r = i;此时&是一个引用声明符

分析两端段代码:

int i= 10;

int j = 20;

int &r = i;

r = j;//引用变量的r实际是以引用变量r中存的内容为地址的变量中的值,实质r就是i

代码二

int a = 10;

int *p = &a;

*P =5;//*运算是以指针变量p中存的内容为地址的变量中的值

97dbd2bbcc12e0d616078c32a62aee71.png引用系统为其分配内存空间,引用变量和指针变量一样都是存址(引用变量中存储的是他所指向的变量的地址)

#include

int main()

{

char k = 'a';

int i = 10;

double j = 20.0;

char &r = k;

char* p = &k;

int &r1 = i;//引用

int *p1 = &i;

double &r2 = j;

double *p2 = &j;

printf("======存地址的变量系统会分配4个字节===\n");

printf("char类型的引用变量分配了%d个字节\nchar*类型的指针变量分配了%d个字节\n",sizeof(&r),sizeof(p));

printf("int类型的引用变量分配了%d个字节\nint*类型的指针变量分配了%d个字节\n",sizeof(&r1),sizeof(p1));

printf("double类型的引用变量分配%d个字节\ndouble*类型的指针变量分配%d个字节\n\n",sizeof(&r2),sizeof(p2));

printf("===引用变量和指针变量中存的都是他们所指向的变量的地址\n");

printf("&k = %Xf\t&r = %Xf\tp = %Xf\n",&k,&r,p);

printf("&i = %Xf\t&r1 = %Xf\tp1 = %Xf\n",&i,&r1,p1);

printf("&j = %Xf\t&r2 = %Xf\tp2 = %Xf\n\n",&j,&r2,p2);

printf("===引用变量名与指针变量*运算后的结果相同===\n");

printf("k = %c\t\tr = %c\t\t*p = %c\n",k,r,*p);

printf("i = %d\t\tr1 = %d\t\t*p1 = %d\n",i,r1,*p1);

printf("j = %lf\tr2 = %lf\t*p2 = %lf\n",j,r2,*p2);

return 0;

}

运行结果:

178b4e385c049f8a9e687ffab91fd232.png

1、引用类型的的变量在声明的同时必须初始化,因为引用似常量所以必须声明时初始化,更没有引用的引用即没有int && i = &j,指针却有指针的指针,因为指针自身的地址我们有读的权限)

#include

int main()

{

int i = 10;

/*

int &r ;//error C2530: 'r' : references must be initialized引用声明时必须初始化

r = i;

*/

int &r = i;//正确

return 0;

}

2、引用的赋值:一般初始化变量时,初始值会被拷贝到新建的对象中,然而定义引用时,程序把引用和它的

初始值绑定在一起,而不是将初始值拷贝给引用,引用即被绑定变量的别名。引用并不是对象(即引用类型的

变量自身的地址我们无法进行读写操作,然后我们就说引用类型变量没有地址(它里面存的是绑定的变量的地址))

相反的,它只是为一个已经存在的对象所起的另外一个名字

#include

int main()

{

int i = 10;

int j = 20;

int &r = i;

r = j;//此时引用(指向)了一个整形变量,此时给r赋值,就是给i赋新值。此时i中的值也是j(即20)

//换句话说把变量j的值赋值给以引用变量i中所存放的内容为地址的变量

printf("i = %d\n",i);

return 0;

}

运行结果:

8731d213b8015f6bf894e1ab6ec418a7.png

2-1、通过函数传参可以实现通过被调函数为主调函数中的变量赋值。形参为引用类型(效率比用指针好)

#include

void rfun(int &r);

int main()

{

int i = 10;

printf("i = %d\n",i);

printf("======调用函数后i的值为======\n");

rfun(i);//通过函数传参来修改主调函数中的变量

printf("i = %d\n",i);

return 0;

}

void rfun(int &r)

{

r = 20;//把20赋值给以引用变量r中存储的内容为地址的变量

}

运行结果:

ae8e5bb7c7c9763f41d98e2f88fa04e4.png

***此处经常变换成函数传引用来代替和普通变量中的函数传值。(如果不希望被调函数改变主调函数

中的变量的值时可使用const关键字。例如函数仅仅用来打印主函数中的信息)

3、一旦初始化完成,引用将和它的初始值一直绑在一起,因为无法将引用绑定到另一个对象,因为引用没有地址,说明他恒定不变,那他就拥有常量的性质,常量声明时必须初始化,而且只能初始化一次

#include

int main()

{

int i = 10;

int j = 20;

int &r = i;

//int &r = j;//报错error C2374: 'r' : redefinition; multiple initialization不能重复初始化

return 0;

}

4、因为引用本身不是一个对象,引用变量没有实际的地址,所以不能定义引用的引用&r(而指针是一个对象,

即指针变量有地址,可以定义指向指针的指针)例如:

#include

int main()

{

int i = 10;

int &r = i;

//int &&r1 = &r;报错不能声明引用的引用,因为引用本身没有地址&r是引用变量中存储的内容(它是引用所指向的变量的地址)

return 0;

}

***通常什么样的类型的变量自身有地址,我们就可以定义什么样类型的引用变量来绑定那个变量,也可以定义什么样类型的指针来指向那个变量。

***什么样类型的变量可以声明为常量?:自身有地址我们把他自身的地址弄成恒定不变那么他就成常量了,或者或什么样类型的变量变量有地址,但是我们访问不到这时候我们说这种类型自身的地址就是恒定不变,例如引用,那么我们也可以把这种类型声明为常量。(《c++primer》中说的)(下一篇讨论)

***5、我们可以定义指向指针的引用(定义一个引用变量来绑定一个指针变量)int *&引用变量名 = 值真变量名;(一级指针引用)int** &r =*p;(二级指针引用);

基础概念(重要的地方说三遍):   int i = 10;

int *p = &i;//指针

int j = 20;

int &r = j;//引用

p:表示指针变量p中所存的值(此处为变量i的地址)

&p:表示指针变量p本身的地址(上一篇指针之多级指针中有介绍)

*p:表示以指针变量p中存储的内容为地址的变量的值(此处为变量i的值)

&r:表示引用变量r中所存放的值(此处为变量j的地址)

r:表示以引用变量r中存储的内容为地址的变量的值(此处为变量j的值)

5.1在指向指针的引用中的各个变量的地址和变量值的关系分析

#include

int main()

{

int i = 10;

int* p = &i;

int* &r = p;//把指针变量p本身的地址(&p)赋值给引用变量r,此时引用变量r中

printf("引用变量中存的值为: &r = %Xf\n指针变量本身的地址为: &p = %Xf\n\n",&r,&p);

printf("======华丽的分割线======\n\n");

printf("变量i的地址为:\t &i = %Xf \n指针变量p中存的值为: p = %Xf\n引用变量所引用的变量的值: r = %Xf\n",&i,p,r);

return 0;

}

运行结果:

849597be2491c5c2de98131dbcd9b13f.png

分析图:引用变量r的地址掌握在编译器手中,程序员无法读取到,&r里面存储的是被引用的目标变量的地址,变量r里面存储的是以被引用的目标变量的地址为地址的值。

4a42068dfa91c63cf6423068d089dd92.png

5.1、通过操作指向指针的引用的变量,从而达到改变指针所指向的变量的值

#include

int main()

{

int i = 10;

int* p = &i;

int* &r = p;

printf("i = %d\n",i);

*r = 30;//实际因引用变量中存放的内容为地址的变量中的值(即真变量p中存放的值)在进行*运算

printf("通过改变指向指针变量的引用的引用变量,来改变指针所指向的变量的值:i = %d\n",i);

return 0;

}

运行结果:

32462e5eb83ba36e1c7a87b512ec8b61.png

5.1.2、用函数实现通过操作指向指针的引用的变量,从而达到改变指针所指向的变量的值。实参是指针变量形参是指向指针的引用

#include

void fun(int* &r)

{

*r = 30;//引用变量就是指针变量的一个别名,【最重点的语句】

}

int main()

{

int i = 10;

int*p = &i;

printf("i = %d\n",i);

fun(p);

printf("用函数实现通过改变指向指针的引用的变量,从而达到改变指针所指向的变量的值:i = %d\n",i);

return 0;

}

运行结果:

fd5133ce1f64fb70b7865a46c7b458ca.png

5.2、通过操作指向指针的引用的变量,从而达到改变指针变量中的值

#include

#include

int main()

{

int *p = NULL;

printf("指针变量的初始值为: p = %X\n",p);

int * &r = p;//【重点语句】

r = (int*)malloc(sizeof(int)*4);【重点语句】

printf("通过操作指向指针的引用变量来改变指针变量中的值: p = %X\n",p);

printf("======华丽的分割线======下面是释放内存这次不讨论===\n");

free(p);//释放动态内存

p = NULL;

if(p != NULL)

{

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

}

return 0;

}

运行结果:

9e4a0fc41b65e8992db7ca8b72af502f.png

5.2.1调用函数通过操作指向指针的引用的变量,从而达到改变指针变量中的(值实参为指针变量形参为指向指针的引用)

#include

#include

void fun(int* &r);//函数的声明此函数的功能是开辟动态内存的

void funDest(int* &r);//释放内存

int main()

{

int *p = NULL;

printf("指针变量的初始值为: p = %X\n",p);

fun(p);

printf("通过操作指向指针的引用变量来改变指针变量中的值: p = %X\n",p);

funDest(p);//释放内存

return 0;

}

void fun(int *& r)

{

r = (int*)malloc(sizeof(int)*4);//引用变量r是主调函数中指针变量p的一个别名【重点语句】

printf("我是在函数中分配的动态内存的首地址: r = %X\n",r);

}

void funDest(int* &r)

{

free(r);

r = NULL;

}

运行结果:

288ec77368a6150a239e297cbf667aea.png

6.1、通过指向指针的指针即多级指针也能改变指针变量中存的值,只不过没有指向指针的引用好用,实参指针变量的地址,形参指向指针的指针

#include

#include

int main()

{

int* p = NULL;

int**q = &p;//二级指针用于指向一级指针【重点语句】

printf("指针变量p中存放的地址为: %X\n",p);

*q = (int*)malloc(sizeof(int)*4);//通过二级指针给一级指针变量赋值【重点语句】

**q = 12;【重点语句不懂可以看上一篇中指针中的多级指针】

printf("malloc函数申请的动态内存首地址为: %X\n",*q);

printf("指针变量p中存放的地址为: %X\n",p);

printf("======下面打印的结果是关于内存释放传哪个值=====传哪个都一样\n");

printf("**q = %d\t*p = %d\n",**q,*p);

//free(*q);

free(p);

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

printf("=====打印的值为-572662307表示已经释放===\n");

return 0 ;

}

运行结果:

a0a8c6575de19d11025c0a93fc111493.png

6.1.1调用函数通过操作多级指针从而来改变一级指针变量中的值

#include

#include

void fun(int**q);//声明开辟内存的函数

void funDest(int**q);//声明释放动态内存的函数

int main()

{

int *p =NULL;

printf("主调函数指针p原来的值: %X\n",p);

fun(&p);

printf("通过函数修改主调函数中指针变量的值后,主调函数中指针变量p的值为: p = %X\n",p);

fun(&p);//释放内存

return 0;

}

void fun(int**q) //通过此函数来改变主函数中的指针变量的值

{

*q = (int*)malloc(sizeof(int));//以q中存放的内容(指针变量p的地址&p)为地址的变量的值(即指针变量p中的值)【重点语句】

printf("通过函数申请的内存首地址为:*q = %X\n",*q);

}

void funDest(int**q)

{

free(*q);释放的是以q中存的内容为地址(即指针变量p的地址)的变量的值(即指针变量p的值)

}

运行结果:

04c7ebd16ebe11d5933e3d318d5e0ff2.png

不能定义指向引用的引用

也不能定义指向引用的指针

6、&运算符和*运算符的讨论

&出现在声明中的&是引用声明符,其他情况当做取地址符

*出现在指针变量前面叫做:对指针变量进行*运算

*出现在一个指向指针的引用的引用变量的前面叫做:对引用变量进行解引用

7,指向数组的引用;

未完待续:

参考资料:《c++primer》

参考博文:引用的本质:https://blog..net/k346k346/article/details/46805159

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值