The C Programming Language(2nd)--笔记--1.10

1.10 外部变量与作用域       

        main函数中的变量(如line,longest)是main函数的私有private或局部local变量。它们声明于main内部,因此没有其它函数能直接访问它们。其他函数声明的函数变量亦如此。例,getline函数声明的变量i与copy函数的i无关。函数中每个局部变量只在函数被调用时存在,在函数执行完退出时消失。这也是其他语言常把这类变量称为自动变量的原因。以后用“自动变量”(automatic henceforth)代表“局部变量”。(第四章将讨论stastic存储类,此类局部变量在多次函数调用之间保持值不变。)

        因自动变量只在函数调用、执行期间存在,因此,在函数的两次调用之间,自动变量不保留前次调用时的赋值,且在每次进入函数时都要显式【adv.清楚明确地,详述地;直截了当地,坦率地;露骨地,不隐晦地】为其赋值。若自动变量没有赋值,则其中存放的是无效值。

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

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

int getline(void);
void copy(void)

/*print longest input line;  Specialized version.*/
main()
{
    int len;
    extern int max;
    extern char longest[];

    max = 0;
    while ((len = getline()) > 0)
        if (len > max) {max = len ;
        copy ();
        }
    if (max > 0)    /* There was a line.*/
        printf("%s", longest );
    return 0;
}

/*   getline: Specialized version*/
int getline(void)
{
    int c, i;
    extern char line[];

    for (i = 0; i < MAXLINE - 1
        && (c = getchar)) != EOF && c != '\n'; ++i)
            line [i] = c;
    if (c == '\n'){
        line[i] = c;
        ++i;
    }
    line [i] ='\0';
    return i;
}

/* copy  Specialized version */
void copy (void)
{
    int i;
    extern char line[], longest[];

    i = 0;
    while ((longest [i] = line[i]) != '\0')
        ++i;
}

 该例的前几行定义了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进行显式声明。详见第四章。

        注意,这节中在讨论外部变量时谨慎地使用了定义(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语言程序中的基本语法错误,如圆括号、方括号、花括号不配对等。要正确处理引号(包括单引号和双引号)、转义字符序列与注释。(如果读者想把该程序编写的完全通用的程序,难度会比较大。)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值