const修饰的变量不能够再作为左值,初始化完成后,值不能被修改。
C与C++中const的区别是什么?c语言中,const修饰的值,可以不用初始化,不叫常量,叫做常变量;
输出30 30 30
C++中: const 定义的类型必须初始化,否则报错,c语言中可以不初始化
#includeint main() { const int a = 10 ; int array[a] = {};//a 是常量,可以定义数组 int *p = (int *)&a; *p = 30; std::cout << a << " " << *p << " " << *(&a);// 10 30 10 return 0; }
输出 10 30 10;a的值并未被修改。
原因: const 的编译方式不同,C语言中,const 就是当作一个变量来编译生成指令的。C++中,如果const 赋值是一个立即数,所有出现const常量名字的地方,都被常量的初始化所替换。
为什么上面的输出a的值是10呢?因为const 在编译阶段,就已经被常量替换,也就是: std::cout << 10 << " " << *p << " " << 10;
运行时:debug调试
a的地址是0x00cffafc.查看内存:
执行完第9行:
a的内存中的值变成1e 也即30;但是本来出现a的地方在编译期已经被替换成10,因此输出a依然是10。
如果不是立即数,则是常变量
#includeconst 与指针int main() { int b = 1; const int a = b ; //int array[a] = {};//报错,a是常变量 int *p = (int *)&a; *p = 30; std::cout << a << " " << *p << " " << *(&a);// 30 30 30 return 0; }
const 修饰的量常出现的错误:
(1)常量不能再作为左值
(2)不能把常量的地址泄露给一个普通的指针或者普通的引用变量
const 与一级指针:const 如果右边没有指针*,则const 是不参与类型的
C++的语言规范:就近原则 const 修饰的是离它最近的类型
(1)const int *p ;离const 最近的类型是(int)const修饰的是*p,*p不能修改值。 可以指向任意int的内存,但是不能通过指针间接修改内存的值
(2)int const *p; *不是类型, 离const 最近的类型是(int) 同(1)
(3)int * const p; 离const最近的类型是(int *)const修饰的是p 不能改变p指向的地址,但是可以修改p指向地址的内容
(4)const int * const p;不能改变p 指向的地址,也不能改变p指向地址的内容
#includeint main() { const int a = 10 ; const int * p = &a;//p指向的地址的内容不能修改 return 0; }
const 如果右边没有指针*的话,const 是不参与类型的,仅表示const修饰的是一个常量,不能作为左值
const类型转化公式:
cont int * <= int * 可以转化
int * <= const int * 是错误的
实例1:
#include#include int main() { int * p = nullptr; int * const p1 = nullptr;//const 右边没有* ,const不参与类型 std::cout << typeid(p).name() << std::endl; std::cout << typeid(p1).name() << std::endl; return 0; }
示例2:
int a=10; int *p1= &a; const int *p2 = &a;// const int * <= int * int *const p3 = &a;// int * <= int * int *p4 = p3;//p3是int * 类型,因此没有问题const与二级指针
const int ** q;离const 最近的类型是int ,修饰的是**q;
int * const *q;离const 最近的类型是int *,修饰的是*q;
int ** const q ;离const 最近的类型是int **,修饰的q;//同时const 右边没有*,q是int **类型。
类型转化公式:
int ** <= const int ** //错误
const int ** <= int ** //错误
const 与二级指针结合的时候,两边必须同时有const 或没有const 才能转换;
int ** <= int * const * 是const 和一级指针的结合,const 右边修饰的* (等同于int * <= const int * )错误的
int * const * <= int ** (等同于 const int * <= int *)可以的
要看const 右边的* 决定const 修饰的是类型
#include引用#include int main() { int a = 10; int * p = &a; const int ** q = &p;//error return 0; }
1 引用是必须初始化的,指针可以不初始化,
2 引用只有一级引用,没有多级引用;指针可以有一级指针,也可以用多级指针
3 定义一个引用变量和定义一个指针变量,其汇编指令是一样的;通过引用变量修改所引用内存的值,和通过指针解引用修改指针指向的内存的值,其底层指令也是一模一样的;
引用的错误用法 int &a = 10;由下面的反汇编可以知道,引用的汇编代码第一步是将引用对象的地址拷贝到寄存器中,10是常量;
#include#include int main() { int a = 10; int * p = &a; int &b = a; std::cout << a << " " << b << " " << (*p) << std::endl; *p = 20; std::cout << a << " " << b << " " << (*p) << std::endl; b = 30; std::cout << a << " " << b << " " << (*p); return 0; }
输出:
反汇编:指针和引用没有区别
lea eax,[a] 将a的地址拷贝到寄存器eax中
mov dword ptr [p],eax 将eax中的值拷贝到p中。
从反汇编中看指针和引用并没有区别。都是拷贝a的地址到p,b 中
对指针和引用赋值,都是一样的:获取地址,然后赋值
4 引用即别名
#include#include int main() { int array[5] = {}; int * p = array; int(&q)[5] = array;//定义一个引用指向数组:引用即别名 sizeof(q) = sizeof(array) std::cout << sizeof(array) << "n" << sizeof(p) << "n" << sizeof(q) << std::endl;//20 5 20 return 0; }
关于定义一个引用类型,到底需不需要开辟内存空间,我认为是需要的,上面的汇编代码中,引用和指针的汇编是一模一样的;C++中只有 const 类型的数据,要求必须初始化。而引用也必须要初始化,所以引用是指针,还应该是 const 修饰的常指针。 一经声明不可改变。
站在宏观角度,引用也就是别名,所以不开辟看空间。
站在微观的角度,引用至少要保存一个指针,所以一定要开辟空间。站在底层实现的角度,站在C++对于C实现包装的角度,引用就是指针。那么既然是指针至少要占用4个字节空间。
左值引用左值:有内存地址,有名字,值可以修改;
如int a = 10;int &b =a;
int &c = 20;//错误 20 是右值,20=40是错误的,其值不能修改,没内存,没名字,是一个立即数;
右值引用1 int &&c = 10; 专门用来引用右值类型,指令上,可以自动产生临时量,然后直接引用临时量 c = 1;
反汇编:
可以看出,右值引用首先把右值存放在一个内存地址中:ebp-4ch,然后的操作和左值引用类似:
如果用左值引用:const int &a=1;它首先定义一个临时量int temp=1;然后const int &a = temp;
汇编代码与右值引用没有区别
2 一个右值引用变量,本身是一个左值,只能用左值引用来引用它;不能用一个右值引用变量来引用一个左值
int && a = 1; a = 10; int &e = a;



