1. 关于const的疑问
(1)const常量的判别准则
①只有用字面量初始化的const常量才会进入符号表,如const int i = 1;
②使用其它变量初始化的const常量仍然是只读变量。如const int a = b;//a为只读变量
③被volatile修饰的const常量不会进入符号表,如volatile const int i = 1;//这时会为i分配内存,且i每次都是从内存中取值。加const只是说明i不能作为左值。
▲在编译期间不能直接确定初始值的const标识符,都被作为只读变量处理。
(2)const引用的类型与初始化变量的类型
①当用变量来初始化与const引用时,如果两者类型相同,则初始化变量成为只读变量。
②当用变量来初始化与const引用时,如果两者类型不同,则将生成一个新的变量,即引用的是另一个新变量,而不是原来的用来初始化引用的那个变量。
【编程实验】const典型问题分析 12-1.cpp
#include <stdio.h>
int main()
{
//实验1:初始化变量的类型与引用类型相同时
const int x = 1;
const int& rx = x;//x与rx的类型相同,所以rx为只读变量,不能作为左值
//但本质上还是变量,引用x的内存值(而不是符号表中的值)
int& nrx = const_cast<int&>(rx);//转为普通引用
nrx = 5; //nrx与rx都引用了x的内存值
printf("x = %d\n", x); //1,从符号表中读取
printf("rx = %d\n", rx); //5,从内存中读取
printf("nrx = %d\n", nrx); //5,从内存中读取
printf("&x = %d\n", &x);
printf("&rx = %d\n",&rx);
printf("&nrx = %d\n",&nrx); //x、rx、nrx地址相同
//实验2:初始化变量的类型与引用类型不同时
char c = 'c';
char& rc =c;
const int& trc = c;//c与trc类型不一致,则会生成一个新的变量,然后trc引用这个新的变量
rc = 'a';
printf("c = %c\n", c); //c
printf("rc = %c\n",rc); //c
printf("trc = %c\n",trc);//a
printf("&c = %p\n", &c);
printf("&rc = %p\n",&rc); //rc与c的地址相同
printf("&trc = %p\n",&trc);//trc是一个新的地址
//实验3:volatlie
volatile const int y = 2; //volatile修饰,分为y分配内存
int* p = const_cast<int*>(&y);//因y被const修饰,不能作为左值
*p = 6; //因y不能作为左值,用来代替y = 6;这样的写法
printf("y = %d\n", y);//6,volatile指示y得从内存中读取
printf("p = %p\n", p);//y的地址
const int z = y; //用变量初始化const常量,z不会进入符号表,z分配内存
p = const_cast<int*>(&z);
*p = 7;
printf("z = %d\n", z);//7,因z没进入符号表
printf("p = %p\n", p);//z的地址
return 0;
}
运行结果:
2. 关于引用的疑问
(1)指针与引用的不同
指针 | 引用 | |
初始化 | 值是一个内存地址,不需要初始化 | 必须在定义时初始化,之后无法代表其它变量 |
访问内存 | 通过指针可以访问对应内存地址中的值 | 对引用的操作(赋值,取地址等)都会传递到其代表的变量上。 |
const修饰 | 被const修饰成常量或只读变量。 如const int* p;//p | const引用,表示其代表的变量具有只读属性。如,const int& a等价于const int* const a; |
(2)从使用C++语言的角度来看,引用与指针没有任何关系。引用是变量的新名字,操作引用就是操作对应的变量。当进行C++编程时,直接站在使用的角度看待引用,
引用与指针毫无关系,引用就是变量的别名。
(3)从C++编译器的角度来看,在编译器内部,使用指针常量来实现“引用”。因此,“引用”在定义时必须初始化。当对C++代码进行调试分析时,在一些特殊情况,
可以考虑站在C++编译器的角度看待引用。
【编程实验】引用典型问题分析 12-2.cpp
#include <stdio.h>
int a = 1;
struct SV
{
int& x;
int& y;
int& z;
};
int main()
{
int b = 2;
int* pc = new int(3);
//将sv各成员初始化为变量a,b,*pc等内存的引用
SV sv = {a, b, *pc};
printf("&sv.x = %p\n", &sv.x);//变量a的地址,全局区
printf("&sv.y = %p\n", &sv.y); //变量b的地址,栈
printf("&sv.z = %p\n",&sv.z); //new出来的地址,堆
//在C++中没有“引用数组”的概念,请看如下分析
//对于数组而言,其内存是连续分布的。当进行&array[1] - &array[0]
//表示前后两个元素的地址相差的值,应等于sizeof(元素的类型)。
//但如果允许定义“引用数组”的话,如下面语句,&array[1]表示第1个元素
//的地址,即元素b的地址(&b),而&array[0]表示&a,显然这两个地址是不连续的。
//所以int& array[]={a, b, *pc};//这样的定义是错误的,C++里不支持“引用数组”
return 0;
}
运行结果:
3. 小结
(1)指针是一个变量,而引用是一个变量的新名字
(2)const引用能够生成新的只读变量
(3)在编译器内部使用指针常量实现“引用”
(4)编译时不能直接确定初始值的const标识符都是只读变量