变量实际上是程序在运行的其值可以改变的内存单元的名字,而常量是在程序执行过程中其值固定不变的内存单元的名字,所以,常量必须在定义时初始化。
如果这块数据(这个常量)从字面上看就能知道它的值,那它叫做“字面常量”。
1
2
3
4
5
6
7
8
9const double PI = 3.14159; PI = 3.14; //错误,PI已存入常数区,不能再改变值 const int SIZE = 3; int const X = 5; //类型名和const可以互换位置 const char C = 'k'; int j = 10; constexpr int j1 = j + 10; //表达式中可以出现在常量定义语句中。 //上面都是字面常量 const char *STR = "Hello";
可以看到 const 和 constexpr 的功能基本相同,都用于定义常量,但存在以下区别:
constexpr变量必须在编译时进行初始化,而const变量的初始化可以延迟到运行时。具体而言,用于初始化constexpr常量的表达式中的每部分纸都是程序运行之前就可以确定的字面值常量。而const无此限制,它只限定了定义的常量在程序运行期间不可被修改,但其初始值即使在运行时才能取得也是可以的。
例如,若size() 函数的功能是计算类型数据的长度,则
1
2
3
4
5
6
7
8const int n = size(); //正确,但n的值的取得是在执行函数时 constexpr int m = size(); //错误,程序编译时,不知道size()的值 const int i = 10; int j = 21; const int i1 = i + 10; //正确 const int j1 = j + 10; //正确 constexpr int i2 = i + 10; //正确,编译时可以知道i的值等于10 constexpr int j2 = j + 10; //错误,j是变量
const、constexpr与指针
const可以与指针结合,由于指针涉及 “ 指针本身和指针所指的对象 ”,因此它与常量的结合也比较复杂,可分为三种情况。
- 第一种是常量指针,即指针是常量,不能被改变,但其所指内存空间是变量,可修改。
-
复制代码1
type *const p;//p是const ——> *p不是
-
- 第二种是指向常量的指针,即指针是变量,可再指向其他内存空间单元,但其所指单元是变量,不能修改。
-
复制代码1
type const *p; 或者 const type *p; //p不是const ——> *p是const
-
- 第三种是指向常量的常指针,指针及所指内存单元都是常量,都不能被修改。
-
复制代码1
const type *const p;//p是const ——> *p也是const
-
实例: (以下代码在某函数内部)
1
2
3
4const int a = 5; int b = 9; const int *pa = &a; int *const pb = &b;
在来一个完整代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#include <iostream> using namespace std; int main() { //char *const p0; //错误,p0是常量,必须初始化 char *const p1 = "dukang"; //正确 char const *p2; //正确 const char *p3 = "dukang"; //正确 const char *const p4 = "dukang"; //正确 //const char *const p5; //错误,p5是常量必须初始化 //p1 = "wankang"; //错误,p1是常量,不可改 p2 = "wankang"; //正确,p2是变量,可改 p3 = "wankang"; //正确,p3是变量,可改 //p4 = "wankang"; //错误,p4是常量,不可改 p1[0] = "w"; //正确 //p2[0] = "w"; //错误,*p2是常量,不可改 //p3[0] = "w"; //错误,*p3是常量,不可改 //p4[0] = 'w'; //错误,*p4是常量,不可改 system("pause"); return 0; }
const 对指针和对象之间的相互赋值具有一定影响:
const对象的地址只能赋给指向const对象的指针,否则引起编译错误。但指向const对象的指针可以指向const对象,也可以指向非const对象
例如:
1
2
3
4
5
6
7int x = 9; const int y = 9; int *p1; const int *p2; p1 = &y; //错误,引起编译错误,若改为 “ p1=&x; ” 则是正确。 p2 = &x; //正确 p2 = &y; //正确
用constexpr 限定指针时,会比const简单很多,它只限制指针变量本身是常量,它与所指变量没有关系
1
2
3int x; const int *p1 = &x; //p1是普通指针,指向的对象是常量 constexpr int *p2 = &x; //而p2是常数指针,指向的对象不是常量
const与引用
在定义引用时,可以用const进行限制,使它成为不允许被修改的常量引用。
例如:
1
2
3
4
5int i = 9; int &rr = i; const int &ir = i; rr = 8; ir = 7;//错误,ir是const引用,不允许通过它修改对应的变量i
const 引用可以用常量初始化,但非const 引用不能用常量初始化。这与编译器的处理方式有关,编译器在实现常量引用时生成了一个临时对象,然后让引用指向这个对象,但该对象对用户而言是隐藏不可知的,不能访问。
1
2
3
4int i; const double &ff = 10.0; const int &ir = i + 10; int &ii = 3; //错误
顶层const 和底层const
- 顶层const其实是指不可被修改的常量对象,此概念可以推广到任意类型的数据类型,它们定义的常量对象都是顶层const;
- 底层const则与指针和引用这样的复合类型有关。所有声明为const的引用都是底层const
- 其中指针比较特殊,既可以顶层const,也可能是底层const。
1
2
3
4
5int i = 0; const double d = 9.0; //ic为顶层const const int ic = 32; //ic为顶层const const int &ri = i; //ri为底层const const int &ric = ic; //ric为底层const
指针实际上定义了两个对象:指针本身和指针所指的对象。
- 当指针本身被限定为常量时,称指针为顶层const;
- 当所指的对象被限定为常量,而指针本身未被限定时,称指针为底层const;
- 当指针和所指对象两者都被限定为常量,称指针为顶层const,对象为底层const。
1
2
3
4
5int i = 0; const int ic = 32; int *const p1 = &i; //p1为顶层const const int *p2; //p2为底层const const int *const p3 = ⁣ //p3为顶层const,(*p3)为底层const
在进行复制操作时,复制顶层const对象与底层const对象存在以下区别。
- 复制顶层const不受影响。由于执行复制时不影响被复制对象的值,因此它是否为常量对复制没有影响。例如,对于上面的语句组,执行以下的复制操作。
-
复制代码1
2i=ic;//正确,ic是一个顶层const,对此操作无影响 p2=p3//正确,p2和p3指向的对象类型相同,p3顶层const部分不影响
-
- 底层const的复制是受影响的。要求黏贴和复制的对象有相同的底层const或者能够转换为相同的数据类型,一般而言,非常量能够转换成常量,反之则不行。例如,对于上面的语句组,执行以下的复制操作。
-
复制代码1
2
3
4
5
6
7p2 = p3; //正确,p2为底层const,p3是顶层也是底层const,且类型相同; p2 = &i; //正确,p2为底层const,&i为int*,且能转换成const int* p2 = ⁣ //正确,p2为底层const,&ic为const int* p2 = &ri; //正确,p2的ri为相同类型的底层const int *p = p3; //错误,p3包括底层const定义,而p没有 const int &r2 = i; //正确,const int&可以绑定到一个普通int上 int &r = ic //错误,普通的int&不能绑定到int常量上
-
最后
以上就是纯情猫咪最近收集整理的关于常量 (constant / constexpr)的全部内容,更多相关常量内容请搜索靠谱客的其他文章。
发表评论 取消回复