📄 教学--第十五章 存储类型、作用域、可见性和生存期.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0045)http://d2school.com/bcyl/bhcpp/newls/ls15.htm -->
<HTML><HEAD><TITLE>教学--第十五章 存储类型、作用域、可见性和生存期</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<STYLE type=text/css>P {
MARGIN: 1px 2px; LINE-HEIGHT: 150%
}
.节标题 {
FONT-WEIGHT: bold; FONT-SIZE: 12pt
}
TD {
FONT-SIZE: 9pt
}
.tdtitle {
FONT-SIZE: 20pt
}
.celltopline {
BORDER-TOP: #000000 1px solid
}
.menucell {
FONT-SIZE: 10pt
}
#glowtext {
FONT-SIZE: 10pt; FILTER: glow(color=red,strength=1); WIDTH: 100%
}
A:link {
FONT: 10pt 宋体; COLOR: blue; TEXT-DECORATION: none
}
A:visited {
FONT: 10pt 宋体; COLOR: purple; TEXT-DECORATION: none
}
A:active {
FONT: 10pt 宋体; COLOR: red; TEXT-DECORATION: underline
}
A:hover {
COLOR: blue; TEXT-DECORATION: underline
}
</STYLE>
<META content="MSHTML 6.00.2900.2180" name=GENERATOR></HEAD>
<BODY leftMargin=0 topMargin=3>
<CENTER>
<TABLE height=154 cellSpacing=4 cellPadding=4 width=760 border=0>
<TBODY>
<TR>
<TD
style="FONT-SIZE: 10pt; TEXT-INDENT: 20px; LINE-HEIGHT: 150%; FONT-FAMILY: 宋体"
width="100%" height=148>
<H2>第十五章 存储类型、作用域、可见性和生存期</H2>
<P><A href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.1">15.1
存储类型</A></P>
<P> <A
href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.1.1">15.1.1
外部存储</A></P>
<P> <A
href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.1.2">15.1.2
静态存储类型</A></P>
<P><A href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.2">15.2
作用域和可见性</A></P>
<P> <A
href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.2.1">15.2.1
局部作用域</A> </P>
<P> <A
href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.2.2">15.2.2 全局作用域 和
域操作符</A> </P>
<P> <A
href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.2.3">15.2.3
作用域嵌套及可见性</A></P>
<P><A href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.3">15.3
生存期</A></P>
<P> <A
href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.3.1">15.3.1
程序的内存分区</A></P>
<P> <A
href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.3.2">15.3.2 动态生存期
</A></P>
<P> <A
href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.3.3">15.3.3
局部生存期</A> </P>
<P> <A
href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.3.4">15.3.4
静态生存期</A> </P>
<P> <A
href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.3.5">15.3.5
局部静态变量</A></P>
<P><A href="http://d2school.com/bcyl/bhcpp/newls/ls15.htm#15.4">15.4
对前15章的一点小结</A></P>
<P> </P>
<P>上一章我们讲了“程序的文件结构”。主要涉及到一个问题,即:A文件中定义的某个变量,如果在B文件也能使用它。其间我们学到一个新关键字:<B>extern</B>,它用来声明一个变量,并且指明这是一个“外来的”的变量。如果你对我说的这些感到陌生,那么你先复习一下上一章。</P>
<P> </P>
<P>这一章,我们正是要从extern说起。</P>
<P> </P>
<H3><A name=15.1>15.1</A> 存储类型</H3>
<P> </P>
<P>存储类型分“外部存储”和“静态存储”两种。</P>
<P> </P>
<H4><A name=15.1.1>15.1.1</A> 外部存储</H4>
<P> </P>
<P>外部存储类型使用 <B>extern</B> 关键字表示。</P>
<P> </P>
<P>上一章我们其实一直在用外部存储类型的变量。</P>
<P> </P>
<P>一个全局变量或函数,如果你需要在其它源文件中可以共用到,那么你必须将它声明为“外部存储类型”。这其实就是上一章我们所讲的内容。这里再举个例子,简要复述一次。</P>
<P>在A.cpp 文件中,有一个全局变量 a,和一个函数: func();</P>
<P>//A.cpp 文件:</P>
<P>...</P>
<P>int a;</P>
<P>void func()</P>
<P>{</P>
<P> ...</P>
<P>}</P>
<P>...</P>
<P> </P>
<P>我们希望在B.cpp 或更多其它文件可以使用到变量a和函数func(),必须在<B>“合适的位置”</B>声明二者:</P>
<P>//B.cpp 文件:</P>
<P>...</P>
<P><B>extern </B>int a; //a
由另一源文件(A.cpp)定义</P>
<P><B>extern </B>void func(); //func 由另一源文件(A.cpp)定义</P>
<P> </P>
<P>a = 100;</P>
<P>func();</P>
<P>...</P>
<P> </P>
<P>这里例子中,“<B>合适的位置</B>”是在B.cpp文件里。其它合适的位置,比如在头文件里的例子,请复习上一章。</P>
<P>另外一点需要得强调一次:<B>函数的定义默认就是外部的,</B>所以上面 func()之前的extern也可以省略。</P>
<P> </P>
<P>在使用extern 声明全局变量或函数时,一定要注意:所声明的变量或函数必须在,且仅在一个源文件中实现定义。</P>
<P>如果你的程序声明了一个外部变量,但却没有在任何源文件中定义它,程序将可以通编译,但无法链接通过。下面是该错误类型的一个例子,大家请打开CB,将下面代码写入完整的一个控制台工程。</P>
<P> </P>
<P>错误一、只有声明,没有定义:</P>
<P> </P>
<P>1、用CB新建一个空白控制台工程,CB将自动生成Unit1.cpp。加入以下黑体部分:</P>
<P> </P>
<P><B>extern void func2();</B></P>
<P>int main(int argc, char* argv[])</P>
<P>{</P>
<P><B> func2();</B></P>
<P> return 0;</P>
<P>}<BR> </P>
<P>2、新建一个单元文件(菜单:New | Unit):Unit2.cpp。在Unit2.cpp后面加入:</P>
<P> </P>
<P>extern int a; //a
由另一源文件(A.cpp)定义</P>
<P>extern void func(); //func 由另一源文件(A.cpp)定义</P>
<P> </P>
<P>void func2()</P>
<P>{</P>
<P> a = 100;</P>
<P> func();</P>
<P>}</P>
<P> </P>
<P>现在按Ctrl + F9,将出现以下错误:</P>
<P><IMG height=37 src="教学--第十五章 存储类型、作用域、可见性和生存期.files/ls15.h1.gif"
width=670 border=0></P>
<P>[Linker Error] 表明这是一个“链接”错误。两个错误分别是说变量a和函数func()没有定义。</P>
<P>(你可能奇怪为什么错误消息里,变量'a'的名字变成了'_a'?这是编译器遵循某些标准,在编译结果上对变量名做了一些改变,我们不必理会)</P>
<P> </P>
<P>请大家想一想,并试一试,如何解决这两个链接错误。</P>
<P> </P>
<P>错误二、有声明,但重复定义</P>
<P> </P>
<P>1、用CB新建一个空白控制台工程,CB将自动生成Unit1.cpp。加入以下黑体部分:</P>
<P> </P>
<P><B>extern void func2();</B></P>
<P> </P>
<P><B>int a;
</B><FONT color=#ff0000>// <--全局变量a在此定义了一次</FONT><B> </B></P>
<P><B>void func() </B><FONT color=#ff0000>//
<<B>-</B>-函数func()在此定义了一次</FONT></P>
<P><B>{</B></P>
<P><B> a = 20;</B></P>
<P><B>}</B></P>
<P> </P>
<P>int main(int argc, char* argv[])</P>
<P>{</P>
<P><B> func2();</B></P>
<P> return 0;</P>
<P>}</P>
<P> </P>
<P>2、和错误一的第2步完全一样:</P>
<P> </P>
<P>extern int a; //a
由另一源文件(A.cpp)定义</P>
<P>extern void func(); //func 由另一源文件(A.cpp)定义</P>
<P> </P>
<P>void func2()</P>
<P>{</P>
<P> a = 100;</P>
<P> func();</P>
<P>}</P>
<P> </P>
<P>3、再新建一个单元文件:Unit3.cpp,在文件后加入:</P>
<P> </P>
<P>int a; //
<--全局变量a在此<FONT color=#ff0000>又</FONT>定义了一次 </P>
<P>void func() // <--函数func()在此<FONT
color=#ff0000>又</FONT>定义了一次</P>
<P>{</P>
<P> a = 20;</P>
<P>}</P>
<P> </P>
<P>现在编译这个含有三个单元文件的工程。这回答的是一个链接“<B>警告/Warning</B>”:</P>
<P><IMG height=20 src="教学--第十五章 存储类型、作用域、可见性和生存期.files/ls15.h2.gif"
width=687 border=0></P>
<P> </P>
<P>警告很长,无非是说全局变量 'a' 在两个模块内重复定义。
对了,func()函数我们不是也重复定义了吗?为什么没有得到警告?这是因为CB对重复定义的函数,将只取其一,然后自动抛弃所有重复项。下面的操作可以看到这一结果。</P>
<P>既然是错误类型只是“警告”,那就是说我们可以硬下心肠不管,继续运行。我们现在来看看,两个func()函数,CB到底用了哪一个?</P>
<P>这里需要在运行前,在两个func()的函数定义处,都设置断点:</P>
<P> </P>
<P>第一个断点:在Unit1.cpp 文件里:</P>
<P><IMG height=85 src="教学--第十五章 存储类型、作用域、可见性和生存期.files/ls15.h3.gif"
width=330 border=0></P>
<P> </P>
<P>第二个断点:在Unit3.cpp 文件里:</P>
<P><IMG height=130 src="教学--第十五章 存储类型、作用域、可见性和生存期.files/ls15.h4.gif"
width=330 border=0></P>
<P> </P>
<P>然后按F9,运行,我们看到断点停在 Unit1.cpp中的 func()定义上:</P>
<P> </P>
<P><IMG height=81 src="教学--第十五章 存储类型、作用域、可见性和生存期.files/ls15.h5.gif"
width=351 border=0></P>
<P> </P>
<P>而另一处:Unit3.cpp 里的断点,变“黄”(无效断点)了:</P>
<P><IMG height=101 src="教学--第十五章 存储类型、作用域、可见性和生存期.files/ls15.h6.gif"
width=334 border=0></P>
<P> </P>
<P>之所以成为无效断点,有两种原因:</P>
<P>其一是某些代码,比如单纯的变量声明:int a; 或如宏定义等,这些代码在编译后成为程序的初始化部分,无需运行。</P>
<P>其二是某些无用,或可优化的代码中编译过程被丢弃。</P>
<P>这里正是第二种情况。</P>
<P> </P>
<P>尽管变量或函数重复定义似乎并不造成“致命”错误,但我们同样需要严加注意,消除所有这类错误。请大家对本例进行改错。</P>
<P> </P>
<H4><A name=15.1.2>15.1.2</A> 静态存储类型</H4>
<P> </P>
<P>静态存储类型使用 <B>static</B> 关键字表示。</P>
<P> </P>
<P>static 关键限定其所修饰的全局变量或函数只能在当前源文件中使用。</P>
<P> </P>
<P>反过来说,如果我们确定某个全局变量仅仅是在当前源文件中使用,我们可以限定它为静态存储类型。</P>
<P> </P>
<P>static 的使用格式 :</P>
<P>static 变量定义或函数定义</P>
<P>如:</P>
<P> </P>
<P>static int a;</P>
<P>static void func();</P>
<P> </P>
<P>举一个例子,下面的代码可以正确编译、运行:</P>
<P> </P>
<P>Unit1.cpp 文件:</P>
<P>...</P>
<P><B>extern</B> int a;</P>
<P>int main(int argc, char* argv[])</P>
<P>{</P>
<P> a = 100;</P>
<P> </P>
<P> return 0;</P>
<P>}</P>
<P> </P>
<P>Unit2.cpp 文件:</P>
<P>...</P>
<P>int a;</P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -