Chapter 4 - Functions and Program Structure(三)

4.4 Scope Rules(作用域规则)

The functions and external variables that make up a C program need not all be compiled at the same time; the source text of the program may be kept in several files, and previously compiled routines may be loaded from libraries. Among the questions of interest are

构成C语言程序的函数与外部变量可以分开进行编译。一个程序可以存放在几个文件中,原先已编译过的函数可以从库中进行加载。这里我们感兴趣的问题有:

 How are declarations written so that variables are properly declared during compilation?

如何进行声明才能确保变量在编译时被正确声明?

 How are declarations arranged so that all the pieces will be properly connected when the program is loaded?

如何安排声明的位置才能确保程序在加载时各部分能正确连接?

 How are declarations organized so there is only one copy?

如何组织程序中的声明才能确保只有一份副本?

 How are external variables initialized?

如何初始化外部变量?

Let us discuss these topics by reorganizing the calculator program into several files. As a practical matter, the calculator is too small to be worth splitting, but it is a fine illustration of the issues that arise in larger programs.

为了讨论这些问题,我们重新组织前面的计算器程序,将它分散到多个文件中。从实践的角度来看,计算器程序比较小,不值得分成几个文件存放,但通过它可以很好地说明较大的程序中遇到的类似问题。

The scope of a name is the part of the program within which the name can be used. For an automatic variable declared at the beginning of a function, the scope is the function in which the name is declared. Local variables of the same name in different functions are unrelated. The same is true of the parameters of the function, which are in effect local variables.

名字的作用域指的是程序中可以使用该名字的部分。对于在函数开头声明的自动变量来说,其作用域是声明该变量名的函数。不同函数中声明的具有相同名字的各个局部变量之间没有任何关系。函数的参数也是这样的,实际上可以将它看作是局部变量。

The scope of an external variable or a function lasts from the point at which it is declared to the end of the file being compiled. For example, if main, sp, val, push, and pop are defined in one file, in the order shown above, that is,

外部变量或函数的作用域从声明它的地方开始,到其所在的(待编译的)文件的末尾结束。例如,如果mainspvalpushpop是依次定义在某个文件中的5个函数或外部变量,如下所示:

main() { ... }

int sp = 0;

double val[MAXVAL];

void push(double f) { ... }

double pop(void) { ... }

then the variables sp and val may be used in push and pop simply by naming them; no further declarations are needed. But these names are not visible in main, nor are push and pop themselves.

那么,在pushpop这两个函数中不需进行任何声明就可以通过名字访问变量spval但是,这两个变量名不能用在main函数中,pushpop函数也不能用在main函数中。

On the other hand, if an external variable is to be referred to before it is defined, or if it is defined in a different source file from the one where it is being used, then an extern declaration is mandatory.

另一方面,如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一个源文件中,则必须在相应的变量声明中强制性地使用关键字extern

It is important to distinguish between the declaration of an external variable and its definition. A declaration announces the properties of a variable (primarily its type); a definition also causes storage to be set aside. If the lines

将外部变量的声明与定义严格区分开来很重要。变量声明用于说明变量的属性(主要是变量的类型),而变量定义除此以外还将引起存储器的分配。如果将下列语句放在所有函数的外部:

int sp;

double val[MAXVAL];

appear outside of any function, they define the external variables sp and val, cause storage to be set aside, and also serve as the declarations for the rest of that source file. On the other hand, the lines

那么这两条语句将定义外部变量spval,并为之分配存储单元,同时这两条语句还可以作为该源文件中其余部分的声明。而下面的两行语句:

extern int sp;

extern double val[];

declare for the rest of the source file that sp is an int and that val is a double array (whose size is determined elsewhere), but they do not create the variables or reserve storage for them.

为源文件的其余部分声明了一个int 类型的外部变量sp 以及一个double 数组类型的外部变量val(该数组的长度在其它地方确定),但这两个声明并没有建立变量或为它们分配存储单元。

There must be only one definition of an external variable among all the files that make up the source program; other files may contain extern declarations to access it. (There may also be extern declarations in the file containing the definition.) Array sizes must be specified with the definition, but are optional with an extern declaration.

在一个源程序的所有源文件中,一个外部变量只能在某个文件中定义一次,而其它文件可以通过extern 声明来访问它(定义外部变量的源文件中也可以包含对该外部变量的extern 声明)。外部变量的定义中必须指定数组的长度,但extern 声明则不一定要指定数组的长度。

Initialization of an external variable goes only with the definition.

外部变量的初始化只能出现在其定义中。

Although it is not a likely organization for this program, the functions push and pop could be defined in one file, and the variables val and sp defined and initialized in another. Then these definitions and declarations would be necessary to tie them together:

假定函数push pop 定义在一个文件中,而变量val sp 在另一个文件中定义并被初始化(通常不大可能这样组织程序),则需要通过下面这些定义与声明把这些函数和变量“绑定”在一起:

in file1:

extern int sp;

extern double val[];

void push(double f) { ... }

double pop(void) { ... }

in file2:

int sp = 0;

double val[MAXVAL];

Because the extern declarations in file1 lie ahead of and outside the function definitions, they apply to all functions; one set of declarations suffices for all of file1. This same organization would also bee needed if the definition of sp and val followed their use in one file.

由于文件file1 中的extern 声明不仅放在函数定义的外面,而且还放在它们的前面,因此它们适用于该文件中的所有函数。对于file1,这样一组声明就够了。如果要在同一个文件中先使用、后定义变量spval,也需要按照这种方式来组织文件。

4.5 Header Files

Let is now consider dividing the calculator program into several source files, as it might be is each of the components were substantially bigger. The main function would go in one file, which we will call main.c; push, pop, and their variables go into a second file, stack.c; getop goes into a third, getop.c. Finally, getch and ungetch go into a fourth file, getch.c; we separate them from the others because they would come from a separately-compiled library in a realistic program.

下面我们来考虑把上述的计算器程序分割到若干个源文件中的情况。如果该程序的各组成部分很长,这么做还是有必要的。我们这样分割:将主函数main 单独放在文件main.c中;将push pop 函数以及它们使用的外部变量放在第二个文件stack.c 中;将getop函数放在第三个文件getop.c中;将getchungetch函数放在第四个文件getch.c中。之所以分割成多个文件,主要是考虑在实际的程序中,它们分别来自于单独编译的库。

There is one more thing to worry about - the definitions and declarations shared among files. As much as possible, we want to centralize this, so that there is only one copy to get and keep right as the program evolves. Accordingly, we will place this common material in a header file, calc.h, which will be included as necessary. (The #include line is described in Section 4.11.) The resulting program then looks like this:



此外,还必须考虑定义和声明在这些文件之间的共享问题。我们尽可能把共享的部分集中在一起,这样就只需要一个副本,改进程序时也容易保证程序的正确性。我们把这些公共部分放在头文件calc.h 中,在需要使用该头文件时通过#include 指令将它包含进来#include指令将在4.11 节中介绍)。这样分割后,程序的形式如下所示:

There is a tradeoff between the desire that each file have access only to the information it needs for its job and the practical reality that it is harder to maintain more header files. Up to some moderate program size, it is probably best to have one header file that contains everything that is to be shared between any two parts of the program; that is the decision we made here. For a much larger program, more organization and more headers would be needed.

我们对下面两个因素进行了折衷:一方面是我们期望每个文件只能访问它完成任务所需的信息;另一方面是现实中维护较多的头文件比较困难。我们可以得出这样一个结论:对于某些中等规模的程序,最好只用一个头文件存放程序中各部分共享的对象。较大的程序需要使用更多的头文件,我们需要精心地组织它们。

 

4.6 Static Variables

The variables sp and val in stack.c, and buf and bufp in getch.c, are for the private use of the functions in their respective source files, and are not meant to be accessed by anything else. The static declaration, applied to an external variable or function, limits the scope of that object to the rest of the source file being compiled. External static thus provides a way to hide names like buf and bufp in the getch-ungetch combination, which must be external so they can be shared, yet which should not be visible to users of getch and ungetch.

某些变量,比如文件stack.c中定义的变量spval以及文件getch.c中定义的变buf bufp,它们仅供其所在的源文件中的函数使用,其它函数不能访问。用static声明限定外部变量与函数,可以将其后声明的对象的作用域限定为被编译源文件的剩余部分。通过static 限定外部对象,可以达到隐藏外部对象的目的,比如,getch-ungetch 复合结构需要共享bufbufp两个变量,这样bufbufp必须是外部变量,但这两个对象不应该被getchungetch函数的调用者所访问。

Static storage is specified by prefixing the normal declaration with the word static. If the two routines and the two variables are compiled in one file, as in

要将对象指定为静态存储,可以在正常的对象声明之前加上关键字static 作为前缀。如果把上述两个函数和两个变量放在一个文件中编译,如下所示:

static char buf[BUFSIZE]; /* buffer for ungetch */

static int bufp = 0; /* next free position in buf */

int getch(void) { ... }

void ungetch(int c) { ... }

then no other routine will be able to access buf and bufp, and those names will not conflict with the same names in other files of the same program. In the same way, the variables that push and pop use for stack manipulation can be hidden, by declaring sp and val to be static.

那么其它函数就不能访问变量bufbufp,因此这两个名字不会和同一程序中的其它文件中的相同的名字相冲突。同样,可以通过把变量sp val 声明为静态类型隐藏这两个由执行栈操作的pushpop函数使用的变量。

The external static declaration is most often used for variables, but it can be applied to functions as well. Normally, function names are global, visible to any part of the entire program. If a function is declared static, however, its name is invisible outside of the file in which it is declared.

外部的static 声明通常多用于变量,当然,它也可用于声明函数。通常情况下,函数名字是全局可访问的,对整个程序的各个部分而言都可见。但是,如果把函数声明为static类型,则该函数名除了对该函数声明所在的文件可见外,其它文件都无法访问。

The static declaration can also be applied to internal variables. Internal static variables are local to a particular function just as automatic variables are, but unlike automatics, they remain in existence rather than coming and going each time the function is activated. This means that internal static variables provide private, permanent storage within a single function.

static也可用于声明内部变量。static类型的内部变量同自动变量一样,是某个特定函数的局部变量,只能在该函数中使用,但它与自动变量不同的是,不管其所在函数是否被调用,它一直存在,而不像自动变量那样,随着所在函数的被调用和退出而存在和消失。换句话说,static类型的内部变量是一种只能在某个特定函数中使用但一直占据存储空间的变量。





 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值