1、const的初步认识
const怎么理解?
- const修饰的变量不能够再作为左值!!!
- 初始化完成后,值不能被修改!!!
- 图示b=30;语句错误。因为const修饰b的值为20,之后,b的值就不能被修改了!
2、C和C++中const的区别是什么?
C中的const
- 我们在C文件中使用const,在C语言,把const修饰的量和普通的量都是变量,只是const修饰的变量不能作为左值。
- 在C语言中,const修饰的量可以不初始化,但是就没有机会再给它合法的值了,因为不能作为左值。
- 在C中,const修饰的量不叫常量,叫做常变量。
下面这个数组写法是错误的,因为a不是常量,是常变量,所以它不能作为常量/常量表达式来使用。
面试问题:
- 指针p指向的是a的内存;
- const只是语法上保证修饰的量a不能作为左值,即不能作为左值修改;
- 但是内存是可以修改的,我们通过指针就可以修改了;
- 我们也可以通过插入汇编指令去修改a的内存;
我们看到,a的内存的值已经被改为30了。
因为在C语言中,a是常变量!!!
C++中的const
- 在C++中,对于const修饰的量是必须要进行初始化的。
可以看到,不初始化是无法通过编译的。
C++的const必须初始化,叫做常量。
- 既然是常量,是可以来定义数组的(因为是常量,所以可以作为数组的下标哦!)
我们把a进行强转看看:
可以发现,打印出来的值和在C文件中打印出来的值不一样。
因为对const的编译方式不同:
- 在C中,const就是当作一个变量来编译生成指令的。
- 但是在C++中,所有出现const常量名字的地方,都被常量的初始化值给替换了!!!
数组定义:
在C++中相当于将数组长度a直接替换了:(是用20在定义数组的长度。)
在这行代码中:
- 出现const常量名字的地方,都直接用常量的值进行替换;
问题:p指针到底有没有修改a的内存值?
- 改了,a的内存值已经被修改成30了。
*p
把a
的内存的值改为30了,但是编译器在编译时是把常量a都用20替换了,和a的内存就没有关系了。- 所以,const修饰必须初始化,不然拿谁替换啊???
我们再来看:
C++文件:
用变量b初始化a,但是变量的值是多少只有在运行的时候才知道,所以我们可以认为,a的地方都用变量b来替换了。
此时的a已经相当于退化变成C语言中的常变量了。不能定义数组了,也不能替换了。
- 在C++中,const初始值是一个常量,则这个就是一个常量;是变量,就是常变量
3、const与一级指针的结合应用
后面给空指针都是用nullptr。
- 因为在C++中,NULL是空指针或者0值。NULL不区分指针和整数。
const修饰的量 叫常量,和普通变量的区别是什么? C++有两点区别?
- 1、编译方式不同 (编译过程中所有出现常量名字的地方,都会用常量的初值来替换)
- 2、不能作为左值了
C++中const修饰的量常出现的错误是:
- 常量不能再作为左值 (不能直接修改常量的值)
- 不能把常量的地址泄露给一个
普通的指针
或者普通的引用变量
(不可以间接修改常量的值) - 这2点是C++编译器可以检测出来报错的。
我们看下面代码:
因为指针没有做任何的修饰,指针解引用可以随便赋值的,会间接修改。
无法编译通过:
那怎么转换呢?
const和一级指针的结合:有4种情况
C++的语言规范:const修饰的是离它最近的类型。
1、const int *p = &a;
const int *p = &a;
-
const修饰的就是int了,因为int本身就可以组成一个类型,不需要再带后面的* 。
-
我们不关心const修饰的是什么类型,我们关心的是const修饰的是什么表达式。什么表达式不能再被赋值了。
-
我们把修饰的类型去掉,就是const修饰的表达式了:*p
-
*p就不能被赋值了。
-
*p被const修饰了,const修饰了指针p的指向,指向的东西不能被赋值了。
-
但是指针本身可以被赋值,因为p本身没有被const修饰。(p=&b是可以的)
-
p可以任意指向不同的int类型的内存,但是不能通过指针间接修改指向的内存的值。
2、int const* p;(和第1个一样的)
int const* p;
- const修饰的是离它最近的类型,但是*不是类型哦,不能用 * 定义变量,所以修饰的还是int。
- const修饰的表达式还是 *p;
3、int *const p = &a;
int *const p = &a;
- 这个const修饰的类型是int *了,因为 * 自己不能单独作为类型,所以再往前走,就是int *了
- 去掉const修饰的类型,就是const修饰的表达式,就是p本身了;
- p本身是常量,不能被修改(p = & c,是错的),但是 *p没有被const修饰,可以修改(*p = 20是可以的)。
- 这个指针p现在是常量,不能再指向其它内存,但是可以通过指针解引用修改指向的内存的值
4、const int *const p = &a;
- 上面的情况的综合
- 前面的const修饰的类型是int,所以const修饰的表达式是*p,*所以 p不能被赋值。
- 后面的 const修饰的类型是int *,所以修饰的表达式是p,所以p不能被赋值。
举例:
这样*p就不能被解引用了。
下面几种写法都可以指向a:
- 总结:
我们看这个是什么情况?
直接先说结果:其实是int* <= int*,const没有参与作用!
我们先来看下面:
- const如果右边没有指针*的话,const是不参与类型的!!!
- const只用来表示q2是常量,q2不能作为左值
其实是int <= int,const没有参与作用!**
下面这两个也是都没有问题的:
总结const和指针的类型转换公式:
左值 = 右值
int* = const int* 是错误的!
const int* = int* 是可以的!
面试案例
int *q=p
是错误的。const int *
不能赋值给int
*
4、const与二级指针的结合应用
左边的类型是const int**,右边的类型是int **
这个类型转换是正确还是错误的???
我们看下面示意图:
对于这个二级指针q,它的表达式组成就这3种:
- q(表示本身的值)
- *q(表示它所指向的一级指针p的值)
- **q(表示它所指向的一级指针p指向的变量a的值)
const和二级指针的结合:(有3种情况)
- 第一种,const修饰的是int类型,所以
**q
不能被赋值,但是* q
可以被修改赋值,q
本身也可以被赋值。 - 第二种,const修饰的是
int *
类型,所以* q
不能被赋值,但是q和
**q`可以被赋值。 - 第三种,const修饰的是
int * *
类型,所以q
不能被赋值,但是*q
和**q
可以被赋值。
总结const和指针的类型转换公式:
左值 = 右值
int* = const int* 是错误的!
const int* = int* 是可以的!
const和多级指针结合的时候,两边都必须要有const,一边有一边没有是不行的
int** = const int** 是错误的!
const int** = int** 是错误的!
int** = int*const*(这个const是和一级指针的结合,后面是1个*) 是错误的!
==》* =const * 所以是错误的!!!
int*const* = int** 是可以的!(*转成const*是可以的)
const* = * 是可以的
我们看:
- p是普通的指针,*q指向p这个普通的指针, * q 和p访问的是同一块内存,如果我对 * q赋值,相当于是把内容写到这块内存中,*q存的是const int *,也就是把整型常量的地址是赋值给 *q,const int b=20,可以把&b赋值给 *q,但是p是普通的指针啊,p可以解引用修改整个整型常量的地址啊,所以肯定不行啊!!!不能把常量的地址泄露给一个普通的指针!!!
相当于把这个整型地址放到普通的一级指针变量里面。
但是常量的地址赋值给普通的指针p(因为p和*q等价)了,这是不行的。
怎么改呢???
解决方法1:
- 此时
* p
不能被赋值了。就可以了。
解决方法2:
让*q不能赋值。
5、总结,笔试题
例1
A错:
B正确:
C正确。 const不参与类型。都是int*
例2
A错误。 把常量的地址给普通的指针了。
B正确。
C正确。
D错误。
调整下,变为:
E错误。