《C程序设计语言(第2版新版)典藏版》 —1.10 外部变量与作用域

举报
华章计算机 发表于 2019/12/06 19:11:31 2019/12/06
【摘要】 本节书摘来自华章计算机《C程序设计语言(第2版新版)典藏版》一书中第1章,第1.10节,作者是[美]布莱恩W. 克尼汉 (Brian W. Kernighan) 丹尼斯M. 里奇 (Dennis M. Ritchie),徐宝文 李志译 尤晋元 审校。

1.10   外部变量与作用域

main函数中的变量(如line、longest等)是main函数的私有变量或局部变量。由于它们是在main函数中声明的,因此其他函数不能直接访问它们。其他函数中声明的变量也同样如此。例如,getline函数中声明的变量i与copy函数中声明的变量i没有关系。函数中的每个局部变量只在函数被调用时存在,在函数执行完毕退出时消失。这也是其他语言通常把这类变量称为自动变量的原因。以后我们使用“自动变量”代表“局部变量”。(第4章将讨论static存储类,这种类型的局部变量在多次函数调用之间保持值不变。)

由于自动变量只在函数调用执行期间存在,因此,在函数的两次调用之间,自动变量不保留前次调用时的赋值,且在每次进入函数时都要显式地为其赋值。如果自动变量没有赋值,则其中存放的是无效值。

除自动变量外,还可以定义位于所有函数外部的变量,也就是说,在所有函数中都可以通过变量名访问这种类型的变量(这一机制同Fortran语言中的COMMON变量或Pascal语言中最外层程序块声明的变量非常类似)。由于外部变量可以在全局范围内访问,因此,函数间可以通过外部变量交换数据,而不必使用参数表。再者,外部变量在程序执行期间一直存在,而不是在函数调用时产生、在函数执行完毕时消失。即使在对外部变量赋值的函数返回后,这些变量也将保持原来的值不变。

外部变量必须定义在所有函数之外,且只能定义一次,定义后编译程序将为它分配存储单元。在每个需要访问外部变量的函数中,必须声明相应的外部变量,此时说明其类型。声明时可以用extern语句显式声明,也可以通过上下文隐式声明。为了更详细地讨论外部变量,我们改写上述打印最长文本行的程序,把line、longest与max声明成外部变量。这需要修改这3个函数的调用、声明与函数体。

image.png

image.png

在该例子中,前几行定义了main、getline与copy函数使用的几个外部变量,声明了各外部变量的类型,这样编译程序将为它们分配存储单元。从语法角度看,外部变量的定义与局部变量的定义是相同的,但由于它们位于各函数的外部,因此这些变量是外部变量。函数在使用外部变量之前,必须要知道外部变量的名字。要达到该目的,一种方式是在函数中使用extern类型的声明。这种类型的声明除了在前面加了一个关键字extern外,其他方面与普通变量的声明相同。

某些情况下可以省略extern声明。在源文件中,如果外部变量的定义出现在使用它的函数之前,那么在那个函数中就没有必要使用extern声明。因此,main、getline及copy中的几个extern声明都是多余的。在通常的做法中,所有外部变量的定义都放在源文件的开始处,这样就可以省略extern声明。

如果程序包含在多个源文件中,而某个变量在file1文件中定义、在file2和file3文件中使用,那么在文件file2与file3中就需要使用extern声明来建立该变量与其定义之间的联系。人们通常把变量和函数的extern声明放在一个单独的文件中(习惯上称之为头文件),并在每个源文件的开头使用#include语句把所要用的头文件包含进来。后缀名.h约定为头文件名的扩展名。例如,标准库中的函数就是在类似于<stdio.h>的头文件中声明的。更详细的信息将在第4章中讨论,第7章及附录B将讨论函数库。

在上述特别版本中,由于getline与copy函数都不带参数,因此从逻辑上讲,在源文件开始处它们的原型应该是getline()与copy()。但为了与老版本的C语言程序兼容,ANSI C语言把空参数表看成老版本C语言的声明方式,并且对参数表不再进行任何检查。在ANSI C中,如果要声明空参数表,则必须使用关键字void进行显式声明。第4章将对此进一步讨论。

读者应该注意到,这一节中我们在谈论外部变量时谨慎地使用了定义(define)与声明(declaration)这两个词。“定义”表示创建变量或分配存储单元,而“声明”指的是说明变量的性质,但并不分配存储单元。

顺便提一下,现在越来越多的人把用到的所有东西都作为外部变量使用,因为似乎这样可以简化数据的通信—参数表变短了,且在需要时总可以访问这些变量。但是,即使在不使用外部变量的时候,它们也是存在的。过分依赖外部变量会导致一定的风险,因为它会使程序中的数据关系模糊不清—外部变量的值可能会被意外地或不经意地修改,而程序的修改又变得十分困难。我们前面编写的打印最长文本行的程序的第2个版本就不如第1个版本好,原因有两方面:其一便是使用了外部变量;其二,第2个版本中的函数将它们所操纵的变量名直接写入了函数,从而使这两个有用的函数失去了通用性。

到目前为止,我们已经对C语言的传统核心部分进行了介绍。借助于这些少量的语言元素,我们已经能够编写出具有相当规模的有用的程序。建议读者花一些时间编写程序作为练习。下面的几个练习比本章前面编写的程序要复杂一些。

练习1-20   编写程序detab,将输入中的制表符替换成适当数目的空格,使空格充满到下一个制表符终止位的地方。假设制表符终止位的位置是固定的,比如每隔n列就会出现一个制表符终止位。n应该作为变量还是符号常量呢?

练习1-21   编写程序entab,将空格串替换为最少数量的制表符和空格,但要保持单词之间的间隔不变。假设制表符终止位的位置与练习1-20的detab程序的情况相同。当使用一个制表符或者一个空格都可以到达下一个制表符终止位时,选用哪种替换字符比较好?

练习1-22   编写一个程序,把较长的输入行“折”成短一些的两行或多行,折行的位置在输入行的第n列之前的最后一个非空格之后。要保证程序能够智能地处理输入行很长以及在指定的列前没有空格或制表符的情况。

练习1-23   编写一个程序,删除C语言程序中所有的注释语句。要正确处理带引号的字符串与字符常量。在C语言中,注释不允许嵌套。

练习1-24   编写一个程序,查找C语言程序中的基本语法错误,如圆括号、方括号、花括号不配对等。要正确处理引号(包括单引号和双引号)、转义字符序列与注释。(如果读者想把该程序编写成完全通用的程序,难度会比较大。)


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。