C++的常量折叠--const和volatile

背景知识

在开始之前先说一下符号表,这个编译器中的东西。下面看一下百度百科中的描述:

符号表是一种用于语言翻译器中的数据结构。在符号表中,程序源代码中的每个标识符都和它的声明或使用信息绑定在一起,比如其数据类型、作用域以及内存地址。
符号表在编译程序工作的过程中不断收集、记录和使用源程序中一些语法符号的类型和特征等相关信息。这些信息一般以表格形式存储于系统中。如常数表、变量名表、数组名表、过程名表、标号表等等,统称为符号表。对于符号表组织、构造和管理方法的好坏会直接影响编译系统的运行效率。

还有一个问题:前面说的似乎很让人烦,既然声明成了const,干嘛还老修改啊?

根据C++标准,对于修改const变量,属于:未定义行为(指行为不可预测的计算机代码),这样一来此行为取决于各种编译器的具体实现(即不同编译器可能表现不同)。

看到了吧,C++标准是不提倡这么玩的。

下面看一下前面说的那个对照程序:

int main()
{
    int i0 = 11;

    const int i = 0;         //定义常量i
    int *j = (int *) &i;   //看到这里能对i进行取值,判断i必然后自己的内存空间
    *j = 1;                  //对j指向的内存进行修改
    printf("0x%p\n0x%p\n%d\n%d\n",&i,j,i,*j); //观看实验效果

    const int ck = 9;     //这个对照实验是为了观察,对常量ck的引用时,会产生的效果
    int ik = ck;

    int i1 = 5;           //这个对照实验是为了区别,对常量和变量的引用有什么区别
    int i2 = i1;

    return 0;
}

同时把i的声明那条语句改成:

const volatile int i = 0;         //定义常量i

我们知道volatile是告诉编译器在翻译源码到汇编语言(机器码)的过程中,不要优化。把源码中对i的访问,翻译成每次都要去内存中抓取数据(这里是在做编译工作,只是把对应的源码翻译成汇编语言),而不是从符号表(常量表)中抓取数据。所以加上volatile关键字的i的访问都是去内存中拿数据,不去常量表中。

现在还有个问题,如果我加上volatile关键字,那么每次对i的访问的语句都被翻译成了”去看内存里的数据“这种行为的汇编语言,不会有常量替换的过程了,那么下面的语句是不是合法了呢?

i = 10;

你修改了常量的值,怎么可能合法呢?但是按照上面的说法对于const volatile int类型的i似乎又是合法的。问题出在了这里:

编译器的一部分工作流程是这样的:语法检测->预编译(宏替换,常量替换,*(&i)恒等于i等优化)->编译。所以i = 10这句话在语法检查这个阶段,看到你对一个const常量赋值,就已经报错了,根本到不了编译这个阶段。

但是有的一些编译器似乎无视这个volatile关键字,下面看一下测试的结果:

(1)看vc6.0的结果:

没有volatile关键字的:

有volatile关键字的:

即在VC++6.0编译环境下,在const变量定义时添加volatile修饰符,与不添加效果是一样的。编译器都采取了优化(甚至把编译器优化选项关闭还是如此,无奈了)。

(2)vs2010和g++的测试结果是一样的:

没有volatile关键字:

有volatile关键字:

所以:不建议修改const变量的值,即使修改也要熟悉当前使用的编译器对于该 未定义行为 是如何解释的

时间: 2024-11-23 09:22:22

C++的常量折叠--const和volatile的相关文章

const常量,常量折叠,字面常量

const int a=10: 涉及到一个叫常量折叠的概念(认为我这说得太简单或者不好理解的可以google一下它获取更多信息), 即编译器虽然会给a分配空间(如果取a的地址进行操作的时候,会强迫编译器进行内存分配), 但是在预编译阶段, 会把所有的a用10替换(这就有点像#define了), 所以虽然&a地址存放的内容改变了, 但是a依然为10. 10是字面常量 编译器实现的时候,可以优化,对int类型的字面常量,直接用立即数代替,这样10就不分配内存空间,但是对于“hello”这样的字符常量

C++高级进阶 第四季:const具体解释(二) 常量折叠

一.文章来由 const具体解释之二 二.const 取代 #define const最初动机就是取代 #define. const 优于 #define: (1) #define没有类型检查,const在编译期(而不是预编译期)做类型检查. (2)const方便调试和定位bug. 所以应该全然用const取代#define 三.头文件里的const (1)要使用const取代#define.相同须要把const定义放进头文件(或其它格式文件,include就可以). 这样通过包括头文件.可把c

C++的常量折叠(一)

前言 前几天女票问了我一个阿里的面试题,是有关C++语言的const常量的,其实她一提出来我就知道考察的点了:肯定是const常量的内存不是分配在read-only的存储区的,const常量的内存分配区是很普通的栈或者全局区域.也就是说const常量只是编译器在编译的时候做检查,根本不存在什么read-only的区域. 所以说C++的const常量和常量字符串是不同的,常量字符串是存储在read-only的区域的,他们的具体的存储区域是不同的. 就好像杨立翔老师在上课举得那个例子(讲的东西忘得差

C++的常量折叠(二)

前面的C++的常量折叠(一)的最后留下了一个问题,那就是在声明i的时候,加上修饰符volatile关键字,发现输出的就不一样了,下面来说一下volatile这个关键字. C/C++中的volatile关键字和const对应,用来修饰变量,通常用于建立语言级别的memory barrier.下面这句话是Stroustrup在"The C++ Programming Language"中对volatile修饰词的说明: A volatile specifier is a hint to a

C++的类型转换:static_cast、dynamic_cast、reinterpret_cast和const_cast(dynamic_cast还支持交叉转换,const_cast将一个类的const、volatile以及__unaligned属性去掉)

在C++中,存在类型转换,通常意味着存在缺陷(并非绝对).所以,对于类型转换,有如下几个原则:(1)尽量避免类型转换,包括隐式的类型转换(2)如果需要类型转换,尽量使用显式的类型转换,在编译期间转换(3)避免使用reinterpret_cast和老式的强制类型转换操作符通常,隐式的类型转换会导致警告,需要重视这些警告,并使用显式的类型转换代替,例如static_cast代替一些算术类型转换. 在C++中,对象的类型通常有如下几种:(一)内置类型,如int ,bool ,枚举类型等(二)自定义类型

【转】C++ 常量类型 const 详解

文章转自:http://www.cnblogs.com/daocaoren/archive/2011/07/14/2106278.html 1.什么是const? 常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的.(当然,我们可以偷梁换柱进行更新:) 2.为什么引入const? const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点. 3.cons有什么主要的作用?(1)可以定义const常量,具有不可变性. 例如: const in

const、volatile、mutable的用法

原文:http://dev.yesky.com/393/3007393.shtml const修饰普通变量和指针 const修饰变量,一般有两种写法: const TYPE value; TYPE const value; 这两种写法在本质上是一样的.它的含义是:const修饰的类型为TYPE的变量value是不可变的.对于一个非指针的类型TYPE,无论怎么写,都是一个含义,即value值不可变. 例如: const int nValue:    //nValue是const int const

常量折叠(转载)

本文转载自http://blog.csdn.net/yby4769250/article/details/7359278 今天回顾了大学这几年学习C++的点点滴滴,在回顾了“常量折叠”这里时,突然困惑了,当时学习这点知识时的理解是:可折叠的常量像宏一样,在预编译阶段对常量的引用一律被替换为常量所对应的值,就和普通的宏替换没什么区别,并且,编译器不会为该常量分配空间.现在回顾起来,当时是多么的天真,被现象迷惑了,常量折叠确实会像宏一样把对常量的引用替换为常量对应的值,但是,并非不给该常量分配空间,

void指针意义、Const、volatile、#define、typedef、接续符

1.C语言规定只有相同类型的指针才可以相互赋值. Void*指针作为左值用于接收任意类型的指针, void*指针作为右值赋给其他指针时需要强制类型转换. 2.在C语言中Const修饰的变量是只读的,本质还是变量. 口诀:左数右指  不可改变: 当Const出现在*号左边时,指针指向的数据为常量. 当Const出现在*号右边时,指针本身为常量. const int* p ; //P可变,P指向的内容不可变 int const * p; //跟上一句一样 int *const p; //p不可变,p