![]() |
#2
sevenchina2007-07-31 16:43
[转]比较基础的帖,希望对大家有帮助 前面已经说过了, const这个关键字可以施加一种约束, 使你不会错改内存的值。不过要是你真的想改变那个值,还是可以的, 麻烦一点而已。比如 为什么C++会设计这样一个转型符呢?是为了给程序员更大的自由。C++的一个设计理念是信任程序员,它假设程序员会知道自己在干什么。当你写出*pa = 100的时候,你可能是不小心。但是当你写出*const_cast<int*>(pa) = 100的时候就代表你清楚自己想做什么了。C++是自由的,但是为了享受这份自由,你会经历很大磨难。而其它很多语言,当自己是保姆,当程序员是小孩子,将很多东西都包装好,这个不准那个也不准。以学自行车为例,C++会放开双手,让你自己去骑,开头当然会跌得很惨,之后你可以骑车到你想去的任何地方;另外一些语言就不同了,在车子后面安装两个小轮子,还不放心,再在前面安装两个,没错是很安全,不过骑不快,遇到窄点的地方就进不去了。C++的自由是争议很大的地方,各人都有不同的观点。 (注: 想了解多点转型符,可以参考More Effective C++条款2, 本文主要讲些基本概念,不会讲太多语法) 那么到底那些常量变量,静态动态,是怎么在内存是怎么布置的呢? 我们知道,数据有不同的性质,有些可读不可写,有些可读可写,有些只可以给系统读写......等等。这样不同性质的数据就分开放到内存不同的位置,这叫分段或者叫分区。分段之后最大的好处是容易实现保护,可以指定从这里开始,到那里结束,这个范围的内存空间的性质,假设是只读的,要是程序执行的时候意图修改,就会触发错误处理。我们经常会将空指针赋值为0, 低端地址通常为系统保留,不可被访问,当想引用地址0中的数据,就会出错。原则上将空指针赋值为不可访问的内存地址就可以了,不一定是0。 下面,介绍一下栈(stack)。学过数据结构的都应该知道,stack是先进后出的。比如叠起来的碟子,只可以在最上面放和最方面取。当放上一个碟子,就会变高。当取下一个碟子,就会变低。最先放的碟子会在最后取出,最后放的会最先取出。放碟子,使碟子升高,这动作叫做push, 取碟子,使碟子降低,这动作叫做pop。 stack这结构用得极其频繁,8086系列的计算机就有相应的机械指令push, pop和相应的寄存器(e)sp (e)bp来支持硬件上的栈。 为得到数据,需要得到它的地址。通常地址都不会直接给出的,会使用基址+偏移的形式。这种形式可以表述为 从某某地方开始,向下或者向上数多少格。所谓基址是一个参考点,偏移是相对参考点而言的。比如 基址为1000, 偏移为5,就得到1005; 基址为1000, 偏移为-5, 就得到995。基址+偏移的形式也用的很广泛,平时接触得最多的算是数组了。 寄存器ebp就表示基址, 通常我们叫它栈底寄存器,其实不用理它叫什么,知道是个参考点就行了。esp就是一个偏移, 相对ebp而言, 指示栈的顶端。push指令会使esp减少(减少意味着距离ebp更远,就是栈顶升高), 跟着在那个地方放数据。pop指令将eip所指的数取出,更着eip增加(增加意味着距离ebp近了,就是栈顶降低)。至于升高或降低多少,就看你的数据有多大了。 EAX | ..... | EAX | ..... | EAX | ..... | EBX | ..... | EBX | ..... | EBX | ..... | |
[转载]关于C++中的常量与变量对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的指向也不可变。请仔细想一想。这个问题困惑了我很久的了。