对C++中的变量与常量的理解
程序运行时,所用的数据首先要被放在内存。内存有两个最基本的属性,一个是它的地址(编号),另一个就是它存储的数据。就如一堆小箱子,编号用来区分到底是用到哪个箱子,数值就如箱子里面放着的东西。
数据放在内存,我们给它一个名字,名字只不过是个符号,符号本身都是没有什么意义的,符号代表的东西才有意义。取了名字之后可以根据名字来方便取回我的数就行了。名字到最后都会影射到地址。可以说,名字是只是给人看的,那个人最可能是你自己,所以为了自己, 也为了别人幸福,请花点心思去取个好名字。
数据放在内存之后,可以分为变量与常量,常者,不变也;量者,数值也。前面已经说了,内存有地址和存储的数据两个最基本的属性,因此常量与变量当然也有两个最基本的属性了,一是它分配到的内存地址,另一个就是地址所指内存里面的数值。常量与变量就是从地址所标内存里面的数值可否变化来区分的。程序执行时数值可变为变量,不可变为常量。常量的数值在程序执行之前已经确定下来的了。当然变量与常量还有其它的要素,比如名字和类型。名字最终会影射到地址,类型可以决定它们的大小和行为。类型有其自身的意义。
从内存里面的数值是否可变可以区分变量与常量。那么从分配的内存地址来看呢,又可以将变量与常量分为 静态(static),动态(dynamic),自动(auto)三种不相同的状态。C++中,关键字const含义为不变(常), 关键字static含义为固定(静)。照我理解(注意,是我自己的理解),常和静,变和动,都是一个意思,指变化和不变化,只不过常和变是对于内存存储的数而言,静和动是对于内存地址而言。
好啦。现在看看静态,动态,和自动到底是什么含义,怎么去区分? 当程序刚启动,系统会分配些栈啊,程序控制块啊,各个段啊之类,这可以算一个准备阶段,在这个准备阶段,程序代码还没有正式执行,常量与常量所需的内存已经分配好了,地址已经确定下来,这就为静态。当程序代码已经执行,才去分配内存,内存地址还没有确定,为动态或者自动。要是代码正在执行,需要内存分配,这一个分配行为由系统全部完成,不用你去操心,就为自动;要是需要程序员自己决定分配的时机,显式调用malloc,new 之类的分配函数,就为动态。概括的说,程序执行之前已经分配好内存,决定好内存地址,为静态; 程序执行之时再分配,分两种情况,1)系统自动完成,为自动类型。2)需要显式调用分配函数,自己决定时机,为动态。
将属性关联到数据的过程,叫做绑定(binding)。如果在程序执行之前,变量与常量的属性已经确定了,就叫静态绑定;要是要等到程序执行之时,属性才被关联,被确定,就叫动态绑定。看看C++的书籍,静态,动态,绑定的概念会老是出现的, 到这里应该有大概的理解了,主要是以属性确立的时机来区分。其实不单是变量,常量,有时候调用什么函数(也就是函数的地址)也需要在程序执行之时才能确定。这时候可以先将函数地址先存起来,或者做成一个表。可能有人问,怎么函数地址也可以放起来的吗? 当然了,函数也需要内存来放,既然放在内存,为了找回它,就一定要得回它的位置,也就地址。对于计算机来说,所有东西都是101010之类的数值,什么都已经没有区别了。既然如此,函数,代码,浮点,对象,跟int之类的整型没有什么两样。int可以先放着,函数为什么不可以?
===================================================================
C++中,const是个很重要的关键字,施加了一种约束。有约束其实不是件坏事情,无穷的权利意味着无穷的灾难。应用了const之后,就不可以改变变量的数值了,要是一不小心改变了编译器就会报错,你就容易找到错误的地方。不要害怕编译器报错,正如不要害怕朋友指出你的缺点,编译器是写程序人的朋友,编译时期找到的错误越多,隐藏着的错误就会越少。所以,只要你觉得有不变的地方,就用const修饰,用得越多越好。比如你想求圆的周长,需要用到Pi, Pi不会变的,就加const,const double Pi = 3.1415926; 比如你需要在函数中传引用,只读,不会变的,前面加const; 比如函数有个返回值,返回值是个引用,只读,不会变的,前面加const; 比如类中有个private数据,外界要以函数方式读取,不会变的,加const, 这个时候就是加在函数定义末尾,加在末尾只不过是个语法问题。其实语法问题不用太过注重,语法只不过是末节,记不住了,翻翻书就可以了,接触多了,自然记得,主要是一些概念难以理解。你想想,const加在前面修饰函数返回值,这时候const不放在末尾就没有什么地方放了。
不过const修饰指针就需要注意一下了。要是修饰的类型不是指针,比如int之类,const放在int之前和int之后是一样的,比如
const int a = 2;
int const a = 2;
有着同样的效果。我自己偏向于第一种写法,其实想想,第二种写法更为合理,表示修饰变量a本身,所以a的值不可变。
当类型为指针时,以星号*为界, const加在左右两边,有不同的意思。
1) const int* pa = &a; (可以写成 int const* pa = &a; 注意是以星号为界)
2) int* const pa = &a;
写法1)表示pa所指向的变量,也就是a的值不可变。写法2)表示pa的指向,也就是pa本身的值不可以变,不可以现在指向a, 跟着指向b.
=======================================
int a = 2;
int b = 3;
const int* pt = &a;
//*pa = 1; Error
pa = &b; OK
=====================================
int a = 2;
int b = 3;
int* const pt = &a;
*pa = 1; OK
//pa = &b; Error
==================================
前面已经说过了,const用来指示内存中的数值不会变。指针本质上是一个地址(编号), 这个编号也需要放在内存。所以pa这个变量放在内存,数值是一个地址。当const在*右边,const直接修饰pa, 表示pa的数值不会变,所以也就不可以改变指向。当const在*左边,就修饰指向的变量,故*pa的值不能变。要是const int* const pa = &a; a的值不可变,pa的指向也不可变。请仔细想一想。这个问题困惑了我很久的了。