深入分析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,这一点和拷贝操作时一样,但是要注意指针和引用的赋值结构。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

C++中const的实现机制深入分析

问题  C语言以及C++语言中的const究竟表示什么?其具体的实现机制又是如何实现的呢? 本文将对这两个问题进行一些分析,简单解释const的含义以及实现机制。  问题分析  简单的说co...

C++中const的实现机制深入分析

转自 http://www.jb51.net/article/32336.htm 问题 C语言以及C++语言中的const究竟表示什么?其具体的实现机制又是如何实现的呢? 本文将对这两个...

【Java基础提高】深入分析final关键字(一)

Java的关键字final通常是指被它修饰的数据是不能被改变的,不想改变可能出于两种理由:设计或效率。以下是本文主要大纲: final数据的使用final参数的使用final方法的使用final...

深入分析volatile关键字

深入分析volatile关键字

Overlapped I/O模型深入分析

原文地址:http://www.cppblog.com/Lee7/archive/2008/01/07/40650.html 简述:     Overlapped I/O也称Asynchronou...

Unity3D导入3DMax模型缩放单位问题深入分析

“Unity3D导入3DMax制作的模型存在100倍缩放比例”,各Unity3D开发者基本都听过吧。 怎么保证3DMax中制作的1m导入Unity3D后还是1m? 为什么会存在100倍...

【C++关键字】const 用法 深入 分析。

原创白话文语言深入解释const的含义,以及关于const,C++程序员需要记住多少。

Java与C#的中只读关键字区别分析:Java的Final和C#的Const,Readonly

Java里面没有readonly关键字,预留了const的关键字,目前还没有实际用途,在Java中,跟这两个关键字比较接近的是final;C#中,两者都存在并可用. 两者修饰的全局变量或局部变量都不能...

情景分析“C语言的const关键字”

C语言中的const一直是C语言初学者心中的痛,这是因为const在不同位置有不同作用,在不同情景有不同角色。 这让初学者摸不清头脑。今天,和大家一起研究一下const,让它的每个角色都“深入人心”!...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

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