深入分析const关键字模型
来源:互联网 发布:软件著作权申请登记 编辑:程序博客网 时间:2024/06/15 21:33
- 前言
- const和define
- 宏
- const
- const是编译期的行为
- const声明占用内存
- const是伪常量
- const和复合类型
- 顶层const和底层const
- 概念
- 拷贝操作
- const和其他关键字
- constexpr
- auto
前言
最近在复习c++ primer,把以前没注意到的都深入研究了一下。
此篇博客的结论都建立于c++11或者c++14的新标准上,编译器为VS2015 community版本,G++可能会有较大出入(这点笔者已经在其他博客上验证)
const和#define
宏
c语言中的宏机制被继承到了c++,宏是一种替换行为,而且是完全的字符替换,发生在预编译期,所以在这种机制下,编译期无法对宏进行任何的类型检查,我们来看汇编中,宏是如何实现的:
#define define_var 100 int temp_num = define_var;008C6478 mov dword ptr [temp_num],64h
可以看到,宏本身是不分配任何内存的,而是在预编译时进行字符替换。
值得庆幸的是,现代的大多数IDE,都能在写代码阶段就判断程序员的大多数错误,当然也包括了宏内部的类型检查。
const
const被使用至今,已经从当初宏的替代品,发展成了一个很复杂的东西,特别是从c++11和14引入弱类型的特性(auto和delctype等关键字)后,const的使用稍不注意就会发生很多错误。
const是编译期的行为
const是一种编译期的行为,在编译期内,具有类型,会执行类型检测,所以他能由编译器指出程序在运行之前的错误,关于这点,我会在后面给出验证。
const声明占用内存
最初const和宏最大的区别可能就在于此,以前有种论调认为const声明的变量位于程序的符号表中,经笔者证明,并非是这样,或者说并非仅仅是这样。
至于const占用内存,我们可以从下面一段汇编中看出来:
const int ci = 0;01081FA8 mov dword ptr [ci],0 const int &i = 0;01081FC2 mov dword ptr [ebp-48h],0 01081FC9 lea eax,[ebp-48h] 01081FCC mov dword ptr [i],eax
我们知道,直接用常量表达式初始化引用是不合法的,但是常量引用是可以是被任意表达式初始化的,我们可以看到程序提前声明了一块空间用于存放“0”,然后再将引用绑定到指定地址。
const是伪常量
const的设计非常奇怪,虽然在编译期进行严格的类型检查,但是却不在运行期给予任何保障,而且最重要的是,允许常指针类型转化为普通指针类型,引用类型与之相同,请看下面的语句:
const int cVar = -100; const int* p = &cVar; int* x = (int*)&cVar; *x = 5; cout << "cVar: " << cVar << endl; cout << "p: " << *p << endl; cout << "x: " << *x << endl; cout << "origin rom: " << &cVar << endl; cout << "p rom: " << p << endl; cout << "x rom: " << x << endl;
输出结果是:
cVar: -100
p: 5
x: 5
origin rom: 00AFF988
p rom: 00AFF988
x rom: 00AFF988
笔者刚看到这一块的时候也觉得很奇怪,有两个奇怪的点:
1、虽然内存相同,但是输出的值却不同,分析汇编之后才得出结论,由于输入输出流的汇编有点麻烦,我们看这一句:
int test = cVar;00912610 mov dword ptr [test],0FFFFFF9Ch
我们可以看到,虽然cVar在初始化的时候分配了内存,但是当编译器在遇到cVar这个字符串的时候,还是采取了和宏相同的方案——进行展开替换。
2、常量的值被指针修改了,其实这也理所应当,const的机制没有对内存有任何的操作,一旦进入运行期,存放const变量的内存却没有任何标记证明它是不可修改的,自然指针会把它当做普通内存来处理,这一点笔者不是很明白语言设计者的思路,为什么要允许这种强制转换?所以c++的灵活性也常常为人所诟病——太过灵活导致太容易出bug。
notice:有的内存块是只能写的(比如main函数之外的const申请的就是这样的内存),这时如果用指针修改常量会发生错误
const和复合类型
const和指针、引用笔者不想多说,因为这些全是一些很生硬的规则,如果读者还不清楚常量指针和 指向常量的指针等知识,可以去翻阅c++primer p54-p57
顶层const和底层const
概念
顶层const和底层const是为了方便常量的赋值,类型推断等操作而提出的概念。
一般来讲,所有非复合类型的常对象,是一个顶层const,表示该对象本身是一个常量。
而复合类型,如果绑定的对象是常量,我们称其为底层const,如果我们说这种绑定关系是恒定不变的,那么叫做顶层const。
const int *p 指向常量的指针,这是底层const
int *const p 常量指针,这是顶层const
const int *const p,第一个const为底层const,第二个是顶层const
由于引用本身就是固定的绑定关系,所以常引用都是底层const
拷贝操作
在拷贝(赋值)操作时,顶层const被忽略,但是拷入对象和拷出对象必须拥有相同的底层const资格,具体如下:
- 普通指针不能直接绑定常对象。
虽然常对象本身是顶层的const,但是关于指针的赋值操作,其实是先让一个寄存器绑定到对象,然后把寄存器的地址赋值给指针,而寄存器是一个底层的const,普通指针没有底层的const,汇编如下:
int originVar = 0;0091249F mov dword ptr [originVar],0 int* pO0 = &originVar;009124A6 lea eax,[originVar] 009124A9 mov dword ptr [pO0],eax
普通的引用不能绑定到常对象上。
这一点和指针理由相同。总结来说,一般情况,非常量可以转换为常量,反之不行。
const和其他关键字
const本身就已经有很多复杂的行为,当它和其他关键字混合使用时,将产生更多的误区。
constexpr
关于constexpr笔者使用得很少,这里提出一点:const int *p指向常量的指针,而constexpr int *p却是指向int的常量指针。
auto
c++11很重要的类型推断特性,当auto用于推断const对象时,会忽略顶层const,保留底层const,这一点和拷贝操作时一样,但是要注意指针和引用的赋值结构。
- 深入分析const关键字模型
- 【C++关键字】const 用法 深入 分析。
- const , volatile 关键字分析
- const关键字的深入理解
- const关键字的深入理解
- 深入分析volatile关键字
- 《C关键字分析》之const
- Overlapped模型深入分析
- 情景分析“C语言的const关键字”
- 情景分析“C语言的const关键字”
- const关键字特性分析(待补充)
- 关键字static和const的作用分析
- 情景分析“C语言的const关键字”
- 情景分析“C语言的const关键字”
- 情景分析“C语言的const关键字”
- const关键字
- const关键字
- const关键字
- 四、线性表(1)
- (8)HTML5-图片
- 学习16位DOS汇编笔记
- linux sed 批量替换多个文件中的字符串
- 文章标题
- 深入分析const关键字模型
- 【DP算法篇之初学】背包问题
- 【Linux】tar.xz的解压命令
- 设计模式(designer pattern)
- 关于俩个string 是否相等
- [leetcode]90. Subsets II
- servlet中service、doGet、doPost等方法关系
- 在Windows下安装pip
- php实现远程操作