深入分析const关键字模型

原创 2016年08月29日 20:59:15

前言

最近在复习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关键字总结

1.const 对象 const int a = 5; 或 int const a = 5; 说明: 1)const对象由const关键字修饰。const对象的值一旦定义就不能修改,因此,con...
  • hulin0229
  • hulin0229
  • 2014年03月27日 23:18
  • 691

深入分析java web技术内幕----读书笔记(六)

session和cookie都是用来保持用户与后端服务器的交互状态。 cookie大小大小受限,并且占带宽。 session不能在多个服务器间共享。 cookie是保存在客户端的一个数据结构 se...
  • a724888
  • a724888
  • 2017年03月19日 11:16
  • 1014

《深入分析JavaWeb技术内幕》读书笔记 一.Web请求过程与架构

最近拜读了大牛许令波大神的著作《深入分析JavaWeb技术内幕》,感觉颇有受益,决定写一写读书笔记,以作总结。第一章,我们就来讲一讲Web请求过程。 随着Web2.0时代的到来,互联网的网络架构已从传...
  • JobsandCzj
  • JobsandCzj
  • 2017年04月26日 11:25
  • 1377

深入分析Java Web技术内幕pdf

下载地址:网盘下载 内容简介  · · · · · · 《深入分析Java Web技术内幕(修订版)》新增了淘宝在无线端的应用实践,包括:CDN 动态加...
  • cf406061841
  • cf406061841
  • 2017年07月31日 09:58
  • 447

读《深入分析Java Web技术内幕》_笔记

近期看了一本javaweb方面的书籍《深入分析Java Web技术内幕》 作者是许令波,阅读过程中了解了诸如tomcat工作原理,java的class文件的解读、java编译器、类加载器classlo...
  • wzq6578702
  • wzq6578702
  • 2015年08月27日 21:26
  • 1502

深入分析JavaWeb技术内幕(修订版)》【PDF】下载

《深入分析JavaWeb技术内幕(修订版)》【PDF】下载链接: https://u253469.pipipan.com/fs/253469-230062569    内容简介 《深入分析Java ...
  • zhiyong499
  • zhiyong499
  • 2017年12月03日 17:47
  • 317

函数中关于const关键字使用的注意事项

我们知道C++中const可以用于修饰普通变量、指针变量、引用变量、函数入参以及函数名,例如://常量 const int nMaxNum = 5; //修饰引用 const int& refIn...
  • xiao3404
  • xiao3404
  • 2017年10月17日 22:14
  • 82

const关键字作用总结

const关键字 const是constant的简写,只要一个变量前面用const来修饰,就意味着该变量里的数据可以被访问,不能被修改。也就是说const意味着“只读”readonly 规则:co...
  • lf1570180470
  • lf1570180470
  • 2017年02月23日 18:33
  • 920

C语言const关键字的四种用法

博客: http://www.cnblogs.com/zhangfeionline/p/5882790.html#include /* gcc中,const是通过编译器在编译的时候执行检查来确保实现的...
  • JQ_AK47
  • JQ_AK47
  • 2016年11月13日 22:19
  • 1553

Overlapped模型深入分析(原理篇)

Overlapped模型深入分析(原理篇) http://blog.csdn.net/wu_yanan2003/archive/2007/02/26/1514872.aspx 简述:     Over...
  • stamhe
  • stamhe
  • 2009年08月21日 16:00
  • 674
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深入分析const关键字模型
举报原因:
原因补充:

(最多只允许输入30个字)