const限定符
零.前言
为了防止程序不小心改掉某个变量的值,或者说定义一个变量,用来当常数,且易修改,我们C语言推出了一个关键字const
对变量的类型加以限定。
而const
对象一旦创建后其值就不能改变,所以const对象必须初始化。
默认情况下,const的作用域设定为仅在文件内有效,当多个文件中出现了同名的const变量时,其实等于在不同文件中,分别定义了独立的变量。对于这个问题解决的办法是:在前面加入extern
关键字。
一.const的引用
1.对const的引用
我们一颗把引用绑定到常量上,称为常量的引用
。与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象,例如:
const int ci = 1024;
const int &r1 = ci; //对
r1 = 42; //不对,因为r1是对常量的引用
int &r2 = ci; //不对,非常量不能引用常量
2.初始化和对const的引用
除了在常量初始化引用时允许用任意能转换成引用的类型的表达式,其他的引用类型必须和引用对象一致。所以允许一个常量引用绑定非常量的对象、字面值甚至表达式。
int i = 42;
const int &r1 = i;
const int &r2 = 42;
const int &r3 = r1 * 2;
//以上都对,而r4是一个对非常量的引用,所以错。
int &r4 = r1 * 2;
3.对const的引用可能是一个非const的对象
简单点,就是对象可能是个非常量的值,所以可以被改变:
int i = 42;
int &r1 = i;
const int &r2 = i;
r1 = 0;
//以上都对,但是r2是一个常量引用,所以不能修改其值
r2 = 0;
二.指针和const
1.指向常量的指针
我们可以定义一个指针,它不能改变所指对象的值,但是可以改变指向对象的地址,这种指针就是指向常量的指针
就像这个例子一样:
const int p = 233;
int *ptr = &p;
const int *cptr = &p;
*cptr = 0; //错误,不能给*cptr赋值
2.const指针
const指针代表的意思和指向常量的指针相反,它可以改变指向对象的值,但是不能改变指向对象的地址。所以它必须被初始化,且初始化完成后,它的值(指向的地址)就不能再改变了。
又例如:
int a = 0;
int b = 42;
int *const aptr = &a;
a = 0;
// 此时 我们的 *aptr 的值就为0
aptr = &b; //会报错
所以还是那句话:为了搞清楚,这些变量到底是什么,推荐从右向左读表达式。
3.顶层const
为了区别一个const它到底指向的是一个常量还是说它本身是个常量,我们便称:本身是一个常量的const为顶层const
,而底层const
则是表示所指向的对象是一个常量。
然而套娃的开始了:
const int c = 233;
const int *p = &c;
const int *const ptr = p; //靠右的const是顶层,靠做的是底层
对对象执行拷贝操作时,顶层的const一般没啥影响,而底层const则需要考虑:拷入和拷入的对象是否具有相同的底层const资格。
四.constexpr和常量表达式
1.常量表达式
常量表达式
指 值不会改变并且在编译过程就能得到计算机结果的表达式。而一个对象或者表达式是不是常量表达式,是由它的数据类型和初始值共同决定的。
const int a = 20;
const int b = a + 10;
//这两个是常量表达式
int c = 27;
//尽管c的初始值是一个常量,但是c却只是一个普通的int变量,所以不是常量表达式
2.constexpr变量
简单归结来说constexppr
是编译器来验证常量的值是否是一个常量表表达式。声明为constexpr
的变量一定是一个常量,且用常量表达式初始化。而新标准运行定义一种特殊的constexpr
函数。着中国魂函数应该足够简单使得编译时就能知道计算结果,然后用该函数去声明constexpr
变量。
constexpr int a = 20;
constexpr int b = a + 20;
//上面两个声明都是常量表达式
constexpr int c = con_size(); //只有当con_size函数是一个constexpr函数时,才不会报错
3.字面值类型
因为定义constexpr
函数的时候,在其编译的时候需要进行计算,所以对于声明它的时候,就必须对类型有所限制。所以这些类型应该是"比较简单"的,所以在目前接触到的数据类型中,算数类型、引用和指针属于字面值类型,定义类、IO库、string类不属于。
虽然指针和引用都能定义成constexpr,但他们的初始值很严格,一个constexpr指针的初始值必须是nullptr或者0
4.指针和constexpr
在constexpr中声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关。
例如:
const int *p = nullptr;
constexpr int *q = nullptr;
这两者有很大的差别,p是一个指向整数型常量的指针,而q被constexpr定义成了一个整数型的指针常量,相当于顶层const。
5.使用constexpr的好处?
首先,常量表达式的概念:在编译期就可以计算出结果的表达式
那么为什么要用常量表达式呢,用常量表达式会有什么好处:
- 允许一些计算只在编译时进行一次,而不是每次程序运行时;
- 编译器可以进行尺度更大的优化;
- 可以用在需求编译期间常量的上下文,例如数组长度等;