指针变量的传值、传址和传引用
文章推荐
阅读之前可以尝试阅读:
我对C语言的理解—指针
malloc 不能返回动态内存
引言
举个例子:在func函数退出后,指针pInt所指的内容*pInt为 12
#include <stdio.h>
//公众号:C语言与CPP编程
int func(int* pRes)
{
if(pRes == NULL)
pRes = new int(12);//分配新的内存空间给指针pRes,并赋值
return 0;
}
int main ()
{
int *pInt = NULL;
int val = func(pInt);
printf("%d\n",*pInt);
return 0;
}
解析:int func(int* pRes)函数的形参是指针类型 int * pRes,在函数体中 new了一块内存并赋值 12,将内存地址赋值给指针 pRes。在main函数中,定义了指针pInt,调用func函数,把pInt作为参数传入func函数中。结果 * pInt并不是 12。
原因:在func函数调用过程中,形参和实参的传递使用了值传递方式,这种情况下,形参变量在函数体内发生了变化,在函数结束之后,形参变量随之释放,不能把变化的结果返回给实参。
可以使用指针传递或者引用传递。想要在函数体内改变pRes的值,并把这个变化返回到main函数中,必须传递pRes的指针。因为pRes本身就是指针,所以应该传递指针的指针,或者指针的引用。
int v = 1;
int *p = &v;'
int *&rp = p;
&说明rp是一个引用。*确定rp引用的类型是一个指针。
因为引用不是对象,故无引用的数组,无指向引用的指针,无到引用的引用:
int& a[5]; // 错误
int&* p; // 错误
int& &r; // 错误
所以修改函数int func(int* pRes);为int func(int *&pRes);
#include <stdio.h>
int func(int* &pRes)
{
if(pRes == NULL)
pRes = new int(12);//分配新的内存空间给指针pRes,并赋值
return 0;
}
int main ()
{
int *pInt = NULL;
int val = func(pInt);
printf("%d\n",*pInt);
return 0;
}
指针的引用
首先要声明的是,C语言中,没有reference(引用)的概念。这是C规范中规定,之所以大家会认为C语言会有引用,是因为在cpp文件中,支持以c形式写代码。因而导致大家误认为c支持引用。并且很多编译器C/C++是共用的C中有C++代码也能过,有时候C和C++的界限不是很清晰。
引用是给已定义的变量起别名,引用在声明的时候一定要初始化
#include <iostream>
using namespace std;
int main()
{
int a = 88;
int &c = a; //声明变量a的一个引用c,c是变量a的一个别名,如果引用,声明的时候一定要初始化
int &d = a; //引用声明的时候一定要初始化,一个变量可以有多个引用
cout<<"a="<<a<<endl;
cout<<"c="<<c<<endl;
cout<<"====================="<<endl;
c=99;
cout<<"a="<<a<<endl;
return 0;
}
C++中引用&与取地址&的区别
- &(引用)用来传值,出现在变量声明语句中位于变量 左边时,表示声明的是引用。
- &(取地址运算符)用来获取首地址,在给变量赋初值时出现在等号右边或在执行语句中作为一元运算符出现时表示取对象的地址
总而言之,和类型在一起的是引用,和变量在一起的是取址。实例如下:
1、引用在赋值=的左边,而取地址在赋值的右边,比如
int a=3;
int &b=a; //引用
int *p=&a; //取地址
2、和类型在一起的是引用,和变量在一起的是取址。 举例同样如上,还有下例:
int function(int &i)
{
} //引用
对于vector,上面2条同样适合
vector<int> vec1(10,1); //initialize vec1: 10 elements, every element's value is 1
vector<int> &vec2 = vec1; // vec2 is reference to vec1
vector<int> *vec3 = &vec2; //vec3 is addresss of vec1 and vec2
指针和引用的区别
对于一个类型T,T就是指向T的指针类型,也即一个T类型的变量能够保存一个T对象的地址,而类型T是可以加一些限定词的,如const、volatile等等。见下图,所示指针的含义
char c = 'a';
char *p = &c; //p里面存放的是c的地址
引用是一个对象的别名,主要用于函数参数和返回值类型,符号X&表示X类型的引用。
int i=1;
int &r = i; //此时i=r=1;
//若执行r=2,此时i=r=2;
int *p = &r; //p指向r;
指针和引用的区别有:
- 首先,引用不可以为空,但指针可以为空。前面也说过了引用是对象的别名,引用为空——对象都不存在,怎么可能有别名!故定义一个引用的时候,必须初始化。因此如果你有一个变量是用于指向另一个对象,但是它可能为空,这时你应该使用指针;如果变量总是指向一个对象,i.e.,你的设计不允许变量为空,这时你应该使用引用。如下图中,如果定义一个引用变量,不初始化的话连编译都通不过(编译时错误)。而声明指针是可以不指向任何对象,也正是因为这个原因,使用指针之前必须做判空操作,而引用就不必。
- 其次,引用不可以改变指向,对一个对象"至死不渝";但是指针可以改变指向,而指向其它对象。说明:虽然引用不可以改变指向,但是可以改变初始化对象的内容。例如就++操作而言,对引用的操作直接反应到所指向的对象,而不是改变指向;而对指针的操作,会使指针指向下一个对象,而不是改变所指对象的内容。
- 再次,引用的大小是所指向的变量的大小,因为引用只是一个别名而已;指针是指针本身的大小,4个字节
- 最后,引用比指针更安全。由于不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,因此引用很安全。对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL,所以不安全。const 指针虽然不能改变指向,但仍然存在空指针,并且有可能产生野指针(即多个指针指向一块内存,free掉一个指针之后,别的指针就成了野指针)
- 总之,用一句话归纳为就是:指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名,引用不改变指向。
传值、传地址、传引用区别和联系
- 传值:实参拷贝传递给形参。就是把实参赋值给形参,赋值完毕后实参就和形参没有任何联系,对形参的修改就不会影响到实参。
- 传地址:把实参地址的拷贝传递给形参。就是把实参的地址复制给形参。复制完毕后实参的地址和形参的地址没有任何联系,对实参形参地址的修改不会影响到实参, 但是对形参地址所指向对象的修改却直接反应在实参中,因为形参指向的对象就是形参的对象。
- 传引用:本质没有任何实参的拷贝,两个变量指向同一个对象。这是对形参的修改,必然反映到实参上。
无论传值还是传指针,函数都会生成一个临时变量,但传引用时,不会生成临时变量
传值时,只可以引用值而不可以改变值,但传值引用时,可以改变值,
传指针时,只可以改变指针所指的内容,不可以改变指针本身,但传指针引用时,既可以改变指针所指的内容,又可以改变指针本身,
引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
参考书籍《C陷阱与缺陷》