⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 教学--第十五章 存储类型、作用域、可见性和生存期.htm

📁 电子书籍(白话c++)非常好用的一本书
💻 HTM
📖 第 1 页 / 共 3 页
字号:
      <P>占用内存的大小:这些数据都必须有已知,且固定的大小,比如一个int变量,大小是4个字节,一个char类型,大小是1个字节。为什么必须这样?因为如果这个数据可以占用的大小是未定的,那么,程序就不可能为自动分配内存。</P>
      <P>初始化:就是这个变量最开始的值是什么?放在数据区里的数据,可以是程序员用代码初始化,比如:</P>
      <P>int a = 100;</P>
      <P>这样,a的值按你意思去办,并初始化为100;但如果你没有写初始的代码,如:</P>
      <P>int a;</P>
      <P>那么,数据区内的数据将被初始化为全是0。</P>
      <P> </P>
      <P>第二、放入堆区的数据。</P>
      <P>生存期:堆内的数据什么时候“生(分配内存)”,什么时候“死(释放内存)”,由程序员决定。</P>
      <P>谁负责生死:当然就是程序员了。C++里,有专门的函数或操作符来为堆里的变量分配或释放内存。程序员通过写这些代码来在需要时,让某个堆里的变量“生”,不需要时,让它“死”。</P>
      <P>占用内存的大小:堆里的数据占用的内存可以是固定的,也可以是可变的。这就是C,C++里最强大也最难学的内容:“指针”所要做事。</P>
      <P>初始化:由程序员完成。如果程序员不给它初始值,则它的值是未定的。</P>
      <P> </P>
      <P>由于程序员掌握着堆区内的数据的“生死大权”,并且决定着该数据占用多少内存。所以在写程序时,必须特别注意这些数据。一不小心就会出错。比如一个数据还没有分配内存呢,你就要使用它,就会出错。更常见的是,一个数据,你为它分配了内存,可是却始终没有为替它释放内存,那样就会造成“内存泄漏”。就算你的程序都退出了,这个数据依然可能“阴魂不散”地占用着内存。</P>
      <P>第三、放入栈区的数据。</P>
      <P>生存期:对比前面的两种,数据区里数据具有永久的生存期,而堆里的数据的生存期算是“临时”的。需要了,程序员写代码产生;不需要了,又由程序员写代码释放。在程序员,临时才需要变量非常多,如果每个变量都由程序员来负责产生、释放,那程序员岂不很累?并且很危险(万一忘了释放哪个大块头的家伙....)。所以,必须有一种机制可以让程序自已来产生和释放某些临时变量。所以,放入堆区的数据是只有程序员才能决定的何时需要,何时不需的临时数据,而栈区数据则是编译器就能决定是否需要的临时数据。 
      当然,要想让编译器能知道数据什么时候需要,什么时候不需要,就必须做一种约定。这正是我们现在讲的“生存期”的语法内容。</P>
      <P>谁负责生死:程序(和数据区的一样)。</P>
      <P>占用内存的大小:固定大小(和数据区的一样)。</P>
      <P>初始化:由程序员完成。如果程序员不给它初始值,则它的值是未定的(和堆区的一样)。</P>
      <P> </P>
      <P>下面是三个区加上代码区的分布示意图:</P>
      <P><IMG height=264 src="教学--第十五章 存储类型、作用域、可见性和生存期.files/ls15.h12.gif" 
      width=150 border=0></P>
      <P> </P>
      <P>现在,我们也比较好回答前面的问题:“为什么存放数据的内存需要分成三个区域”?原因正在于程序所要用到的数据具有不同的生存期要求,所以编译器把它们分别放到不同空间,好方便实现要求。</P>
      <P> </P>
      <P>生存期和作用域的关系是:如果一个变量已经没有了生存期,那么自然它也就没了有作用域。但反过来,如果一个变量出了它的作用域,它并不一定就失去了生存期。典型的如函数内的静态数据,下面会讲到。</P>
      <P> </P>
      <H4><A name=15.3.2>15.3.2</A> 动态生存期</H4>
      <P> </P>
      <P>就是放在“堆区”的数据。这些数据是在程序运行到某一处时,由程序员写的代码动态产生;后面又由程序员写的代码进行释放。我们现在还没有学习如何为变量(指针变量)分配和释放的内存的知识。</P>
      <P> </P>
      <H4><A name=15.3.3>15.3.3</A> 局部生存期</H4>
      <P> </P>
      <P>这里的局部和前面讲“局部作用域”一致,都是指“<B>一对{}括起来的代码范围</B>”。</P>
      <P>请看下面代码,并思考问题:</P>
      <P> </P>
      <P>//从前,有一个函数……</P>
      <P>void func()</P>
      <P>{</P>
      <P>&nbsp;&nbsp; //函数内,有一个局部变量……</P>
      <P>&nbsp;&nbsp; int a;</P>
      <P>&nbsp;&nbsp; </P>
      <P>&nbsp;&nbsp; cout &lt;&lt; a &lt;&lt; endl;&nbsp;&nbsp; </P>
      <P> </P>
      <P>&nbsp;&nbsp; a = 100;</P>
      <P>}</P>
      <P> </P>
      <P>//看清楚了,上面输出 a 的值的语句, 位于给a赋值之前!</P>
      <P>//然后,下面的代码是两次调用这个函数:</P>
      <P>...</P>
      <P>func();</P>
      <P>func();</P>
      <P>...</P>
      <P>第一次调用,我们知道屏幕肯定是要输出一个莫名其妙的数,因未初始化的局部变量,其值是不定的。我们以前讲变量时,就做过实例。现在,这里的变量a被输出后,我们让赋于它100。再接下来,我们又调用了一次函数func();请问这回输出的值,是100呢?或者仍然是莫名其妙的数?</P>
      <P> </P>
      <P>大家打开CB,把这个例子做做。注意,动手生成一个空白的控制台工程后,调用func()的那两行代码,要放到主函数main()内,形如:</P>
      <P>……</P>
      <P>int main(int argc, char* argv[])</P>
      <P>{</P>
      <P>&nbsp;&nbsp;&nbsp; func();</P>
      <P>&nbsp;&nbsp;&nbsp; func();</P>
      <P> </P>
      <P>&nbsp;&nbsp;&nbsp; ……</P>
      <P>}</P>
      <P> </P>
      <P> </P>
      <P>正确答案应该是:“仍然是莫名其妙的数”。尽管在第一次调用时func()时,局部变量最后被赋值为100;但很可惜,出了函数这个作用域,a 
      立即就死掉了……第二次再调用函数func()时,那个像个a投胎转世的婴儿,一切又重新开始……它又是一个没有被赋值的的变量了。</P>
      <P> </P>
      <P>请大家把本例中的变量a改为全局变量,并且在函数func()的定义之前定义。再试一试。</P>
      <P> </P>
      <H4><A name=15.3.4>15.3.4</A> 静态生存期</H4>
      <P> </P>
      <P>就是放在“数据区”里的数据。程序一运行时,它们就开始存在;程序结束后,它们自动消亡。</P>
      <P>这里讲的“静态”,和前面的“静态存储类型”不是一个意思。(老师,我忘了什么叫“静态存储类型”?呵,这有可能,本章的内容互相都有些关联和相似,大家多看几遍本章,最主要是课程让你动手的地儿,你就动手做,正所谓“该出手时就出手手……”)。</P>
      <P>“静态存储类型”是指:一个全局变量,它被加上static之后,就只能在本文件内使用,别的文件不能通过加extern的声明来使用它。</P>
      <P>“静态生存期“是指:一个变量,它仅仅产生和消亡一次(即在程序运行时产生,在程序退时消亡),而不像“动态生存期”或“局部生存期”那样可以生生死死,不断“投胎转世”。</P>
      <P> </P>
      <P>下面的代码演示了“静态生存期”和“局部生存期”变量的不同。请你看完以后,回答问题。</P>
      <P> </P>
      <P>#include &lt;iostream.h&gt;</P>
      <P> </P>
      <P>//定义,声明一个全局变量:</P>
      <P>int a;</P>
      <P> </P>
      <P>//声明一个函数,定义在后面</P>
      <P>void func();</P>
      <P> </P>
      <P>int main(int argc, char* argv[])</P>
      <P>{</P>
      <P>&nbsp;&nbsp; int b = 100;</P>
      <P> </P>
      <P>&nbsp;&nbsp; a = 10;</P>
      <P>&nbsp;&nbsp; cout &lt;&lt; a &lt;&lt; end;</P>
      <P>&nbsp;&nbsp; cout &lt;&lt; b &lt;&lt; end;</P>
      <P> </P>
      <P>&nbsp;&nbsp; //调用函数func:</P>
      <P>&nbsp;&nbsp; func();</P>
      <P>}</P>
      <P> </P>
      <P>//func()的定义:</P>
      <P>void func()</P>
      <P>{</P>
      <P>&nbsp;&nbsp; cout &lt;&lt; a &lt;&lt; endl;</P>
      <P>&nbsp;&nbsp; cout &lt;&lt; b &lt;&lt; endl;</P>
      <P>}</P>
      <P> </P>
      <P>哪里有错呢?请大家想想,试试。</P>
      <P> </P>
      <H4><A name=15.3.5>15.3.5</A> 局部静态变量</H4>
      <P> </P>
      <P>//从前,有一个函数……</P>
      <P>void func()</P>
      <P>{</P>
      <P>&nbsp;&nbsp; //函数内,有一个局部变量……</P>
      <P>&nbsp;&nbsp; int a;</P>
      <P>&nbsp;&nbsp; </P>
      <P>&nbsp;&nbsp; cout &lt;&lt; a &lt;&lt; endl;&nbsp;&nbsp; </P>
      <P> </P>
      <P>&nbsp;&nbsp; a = 100;</P>
      <P>}</P>
      <P> </P>
      <P>//调用两次:</P>
      <P>func();</P>
      <P>func();</P>
      <P> </P>
      <P>同样是这个例子,我们只是要把 int a 之前加上一个 static 关键字:</P>
      <P>void func()</P>
      <P>{</P>
      <P>&nbsp;&nbsp; //函数内,有一个<B>局部静态变量</B>……</P>
      <P>&nbsp;&nbsp; <B>static</B> int a;</P>
      <P>&nbsp;&nbsp; </P>
      <P>&nbsp;&nbsp; cout &lt;&lt; a &lt;&lt; endl;&nbsp;&nbsp; </P>
      <P> </P>
      <P>&nbsp;&nbsp; a = 100;</P>
      <P>}</P>
      <P>...</P>
      <P>func();</P>
      <P>func();</P>
      <P>...</P>
      <P> </P>
      <P>我们要问的也是同样一个问题:第二次调用 func()后,输出的 a 值是多少?</P>
      <P>这回答案是:输出的值是100。</P>
      <P>这就是局部静态变量的特殊之处:尽管出了函数的作用域之后,变量已经不可见,并且也失去了作用。但是,它仍然存在着!并且保留着它最后的值。因此,它也是静态生存期。它也只在程序结束之后,才失去生存期。</P>
      <P>上面讲的是局部静态变量“死”的问题,它也只“死”一次,对应地,显然它也只能“生”一次。</P>
      <P>void func()</P>
      <P>{</P>
      <P>&nbsp;&nbsp; <B>static</B> int a = 30;&nbsp; 
//在定义时,同时初始化该局部静态变量为30。</P>
      <P>&nbsp;&nbsp; </P>
      <P>&nbsp;&nbsp; cout &lt;&lt; a &lt;&lt; endl;&nbsp;&nbsp; </P>
      <P> </P>
      <P>&nbsp;&nbsp; a = 100;</P>
      <P>}</P>
      <P>...</P>
      <P>func();</P>
      <P>func();</P>
      <P>...</P>
      <P> </P>
      <P>这回要问的是第一次调用func()时,输出的是什么?第二次呢?</P>
      <P>答案:第一次输出30,第二次输出100,以后若有第三次,第四次,也是输出100。这就是说,初始化:static int = 
      30;这一句,仅被执行一次!</P>
      <P> </P>
      <P>好,假如代码是这样子呢?</P>
      <P>void func()</P>
      <P>{</P>
      <P>&nbsp;&nbsp; <B>&nbsp;static</B> int a;</P>
      <P><B>&nbsp;&nbsp; a = 30;</B>&nbsp; //改成不是在定义时同时初始化</P>
      <P>&nbsp;&nbsp; </P>
      <P>&nbsp;&nbsp; cout &lt;&lt; a &lt;&lt; endl;&nbsp;&nbsp; </P>
      <P> </P>
      <P>&nbsp;&nbsp; a = 100;</P>
      <P>}</P>
      <P>...</P>
      <P>func();</P>
      <P>func();</P>
      <P>...</P>
      <P> </P>
      <P>请回答我,这回,两次调用func()分别输出什么?</P>
      <P> </P>
      <P>有关内存的堆、栈内容,最近我曾在CSDN上做过回答。大家如有兴趣,不妨去看看。(别忘了,我叫“nanyu”)。要学习编程,<A 
      href="http://www.csdn.net/" 
      target=_blank>CSDN</A>是不错的地方。大家有空常去(最好注册个ID)。我因为太忙,去不了几次。</P>
      <P><A 
      href="http://expert.csdn.net/Expert/topic/1255/1255577.xml?temp=.1724665" 
      target=_blank>http://expert.csdn.net/Expert/topic/1255/1255577.xml?temp=.1724665</A></P>
      <P> </P>
      <H3><A name=15.4>15.4</A> 对前15章的一点小结</H3>
      <P> </P>
      <P>一般教材用6或7章的内容,我们扩展成15章。大家可能嫌我讲得太慢。这我承认。哎,我的事情太多了。特别是最近这一段时间,先是笔者得了肾结石,剧痛了一夜最后住入医院,接着是我的宝宝发高烧,我在医院里守了一宿,未料到我的爱人接着也 
      得上医院……可恨的是电信局里不知哪个家伙一时兴起,来个什么“改线”,改来改去也不知改错了什么,我的ASDL就一断数天……幸好我天天打电话免费(不,是倒贴电话费 
      啊)为他们培训什么叫ADSL。(其实我也不懂,不过我总得旁敲侧击地暗示他们,“你们是不是动了什么啊?好好想想?再想想?”,哎,总的来说,他们的客户服务态度还是很好的……)。</P>
      <P> </P>
      <P>不管怎样,在此我向所有付费报名的学员致歉。</P>
      <P> </P>
      <P>这15章的内容,属于C,C++的基础知识,其中有些更是基础中基础。从第16章(数组)开始,就开始中级或高级的内容了。这些新的内容都有一个特点:都和内存地址有着千丝万缕的关系。所以大家有时间抓紧把前面的都复习中,其中犹其是要把我讲到的,有关“变量和内存”关系,全部重新消化一遍。</P>
      <P> </P>
      <P>明天就是冬至了。圣诞节即到,在此我向大家问个节日快乐,并感谢早早给我发来贺卡的几位同学。</P>
      <P> </P>
      <P>更重要的是春节即将到来,但愿我们能在春节前学完C++。然后一起向更为精彩的 Windows编程世界出发。</P>
      <TABLE style="BORDER-COLLAPSE: collapse" borderColor=#111111 height=103 
      cellSpacing=0 cellPadding=0 width="100%">
        <TBODY>
        <TR>
          <TD height=103>
            <P align=center>
            <SCRIPT language=JavaScript>var dToDay = new Date();var isSDJ = (dToDay.getMonth() == 11 && dToDay.getDate() >= 20 && dToDay.getDate() <= 27);function Alors(){    dc =  255 / Nb;    x=StartOffset;	for(i=0;;i++){	    R = 255 * Math.random();// i * dc;	    G = 255 * Math.random(255);//255 - x * dc;	    B = 255 * Math.random(255);//(StartOffset - x) * dc;	    		    Ob=document.all("L"+x);		Ob.style.fontSize = 32;//i*100/Nb + 9;		Ob.style.color="rgb(" + R + "," + G + "," + B + ")";				x++;				if(x >= Nb)			x = 0;					if(x == StartOffset)			break;	}Ob.style.fontSize = 64;	StartOffset++;if(StartOffset >= Nb)	StartOffset = 0;}function SDJKL(){  Phrase="祝学员圣诞节快乐!";  Balises="";  StartOffset=0;  Nb=Phrase.length;  for (x=0;x<Nb;x++){    Balises=Balises + '<span Id=L' + x + ' STYLE="height:100%font-size:32px;font-family: Courier New;font-weight:bold;position:relative;z-index:0">' + Phrase.charAt(x) + '</span>'    }document.write (Balises);Time=window.setInterval("Alors()",600);}if(isSDJ)	SDJKL();</SCRIPT>
            </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></CENTER></BODY></HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -