1. 指针:
指针是一个变量(对象),它的内容是一个地址,指向内存的一个存储单元。通过修饰符“*”来表示,解引用 *p 来表示指针p所指对象的值。指针在逻辑上是独立的,可以改变,比如指针变量的值和指针变量指向的内存的值都是可以改变。不同类型的指针可以指向特定类型的对象,即指针的类型要与所指向的对象的类型一致。可通过指针的指向来改变所指向的对象的值。
const指针:
- 常量指针
const int *p;
int a = 20;
int b = 30;
p = &a;
p为常量指针,常量指针不一定要指向常量,既可以指向常量也可以指向变量,常量指针的含义是不能通过p(常量指针)来改变所指向对象的值。但是并不代表不能通过其他的途径来改变对象的值。
要点:
- 常量指针不允许通过自身来改变对象的值
- 常量指针指向的地址可以改变
例如:
const int *p;
int a = 10;
int b = 20;
p = &a;
*p = 60; 错误//不能通过常量指针p来修改对象a的值
int *p2 = &a;
*p2 = 40; //通过p2修改了a的值,进而p1指向的内容也修改了
p = &b; //p开始指向a的地址,现在让p指向b的地址
2.指针常量
int a = 10;
int* const p = &a;
p为指针常量,p所指向的地址不可改变,即
p只能初始化,不能改变p所指向的对象,但是对象的值可以通过p来改变。
2. 引用:
引用是一个变量的别名,用“&”来修饰,此处的“&”并不是取地址符,而是代表复合类型。引用不是定义一个新的变量,而是给一个已经定义的变量重新起一个名字,引用变量指向的是原变量在内存中的地址。引用在逻辑上是不独立的,它的存在具有依附性,所以一开始就得进行初始化,并且在整个生命周期中不能被改变,“从一而终”,可以通过引用来改变变量的值。
//指针和引用
int main()
{
int a = 10;
int c = 20;
int *p = &a; //p指向a在内存中的地址,p是一个独立的变量
int &b = a; //b是a的别名,b指向a在内存中的地址,b依附于a而存在
int &d = c;
printf("变量 a=%d, %p\n", a, &a);
printf("引用变量 b=%d, %p\n", b, &b);
printf("变量 c=%d, %p\n", c, &c);
printf("引用变量 d=%d, %p\n", d, &d);
printf("指针变量 p=%d, %p\n\n", *p, p);
c = *p; //将p所指向的内存的内容赋给a
printf("c=%d, %p\n", c, &c);
printf("指针变量 p=%d, %p\n\n", *p, p);
*p = 30; //将p所指向的内存的内容重新赋值为30,通过指针来改变变量的值
printf("a=%d, %p\n", a, &a);
printf("b=%d, %p\n", b, &b);
printf("指针变量 p=%d, %p\n\n", *p, p);
b = 50; //b是a的别名,对b进行修改,就是对a进行修改
d = a; //d是c的别名,不能将一个变量赋给引用变量
printf("a=%d, %p\n", a, &a);
printf("b=%d, %p\n", b, &b);
printf("c=%d, %p\n", c, &c);
printf("d=%d, %p\n", d, &d);
printf("指针变量 p=%d, %p\n", *p, p);
return 0;
}
引用的特点:
- 一个变量可以取多个别名
- 引用必须初始化
- 引用只能在初始化的时候引用一次,以后不能再绑定其他的变量
- 引用只能绑定在对象(变量)上,不能与表达式的结果或字面值绑定在一起,例如:int &a = 10; 是错误的
const引用(常引用):
const引用是指----指向“常量”的引用,其实这里的“常量”和常量指针的含义是一样的,const引用可以和常量绑定,也可以和变量绑定,只是不能通过const引用来改变绑定对象的值。
非const引用不能与const对象绑定。因为常量a(const对象)的值不可改变,但却可以通过非const引用来改变常量a(const对象)的值,这是不正确的。
const int a = 10;
int &b = a; //错误,非const引用不能与const对象绑定
b = 20; //这是不正确的
const引用和非const引用的区别:
const引用只读不可修改,与绑定对象是否为const无关。
- 非const引用可读可改,只能和非const对象对象绑定
- 非const引用只能绑定到与该引用同类型的对象,const引用则可以绑定到不同但相关的类型的对象
- const引用可以初始化为不同类型的对象或者初始化为右值,如字面值常量
example:
int main()
{
int a = 10;
int &a1 = a; //非const引用
cout<<"a1= "<<a1<<endl;
const int &a3 = 10; //初始化为右值
cout<<"a3= "<<a3<<endl;
const int &a4 = a+10; //初始化为右值
cout<<"a4= "<<a4<<endl;
double d = 1.20;
const int &a5 = d; //初始化为不同但相关的类型
cout<<"a5= "<<a5<<endl;
return 0;
}
注意:
- 如果是对一个常量进行引用,则编译器首先会建立一个临时变量,然后将该常量的值置入临时变量中,对该引用的操作就是对该临时变量的操作。
- const引用表示,试图通过引用去改变其引用的对象的值时,编译器会报错。但这并不意味着,此引用所引用的对象也因此变成const类型了。我们仍然可以改变其指向对象的值,只是不通过引用。
int main()
{
int a = 10;
const int &b = a;
//b = 20; //出错
a = 20; //通过a间接的修改了const引用b的值
cout<<"b= "<<b<<endl;
return 0;
}
const引用并不是说不可以修改,只不过不能通过const引用来修改,但可以通过其所引用的变量来间接修改const引用变量,这里说的const只是对程序员的一种约束,不要试图去修改const引用修饰的变量。
3. 指针和引用的区别:
- 引用只能在定义的时候初始化一次,之后不能改变其指向,从一而终;指针变量的指向可以改变。
- 引用必须指向有效的变量;但是指针可以为空。
- sizeof 指针对象和引用对象的意义不一样。sizeof引用得到的是所指向的变量的大小;而sizeof指针是对象地址的大小。
- 指针和引用自增(++)和自减(--)。
- 相对而言,引用比指针更安全。指针比引用更灵活,但是也更危险。使用指针时一定要注意检查指针是否为空。指针所指的地址释放以后最好置NULL,否则可能存在野指针的问题
野指针:指针所指向的地址不能进行操作,即指向一个没有访问权的内存区。
产生原因:
- 指针变量定义的时候未初始化。指针变量在创建的时候应当被初始化为NULL,或者让它指向一个合法地址。如果没有进行初始化,它的缺省值是随机的,指向一个不知道的内存空间。
- 指针释放之后未置空。指针使用free或delete释放之后,只是切断了指针和内存块之间的指向关系,并没有把指针本身干掉。指针指向的就是未知内存,在后面使用这个指针的时候,它的值是随机的,有可能会造成意想不到的后果。