• Keine Ergebnisse gefunden

Scope and Qualification

Im Dokument Turbo Pascal® (Seite 139-142)

Continue to step through Sort until it's done. If you get impatient, you can choose Run/Run (Ctrl-F9) or put the cursor on the final end in Sort, then press F4. Note that L is completely sorted, with the values ordered from lowest to highest. The Watch window will look something like this (the values of L will, of course, vary from execution to execution):

• K: 10 Min: 9 Top: 9

L: (19,43,66,202,262,285,396,473,803,936)

Now press F7 again, so that you return to the main body of DoSort. Your Watch window now looks like this:

• K: Unknown identifier Min: Unknown identifier Top: Unknown identifier L: Unknown identifier

What happened? A second ago, these expressions had values, and now they've disappeared altogether. Why? Because, as we mentioned earlier, they're all local to Sort, that is, they are only "visible" and usable while Sort is executing.

The area where an identifier (constant, type, variable, procedure, function) is visible and usable is its scope. The formal definition and rules of scope are given in Chapter 1 of the Reference Guide, "Tokens and Constants," but here's a simple explanation: An identifier's scope goes from the point of its declaration to the end of the program, procedure, or function in which it was declared. (The obvious exception is any identifier declared in the interface section of a unit. When a unit is used, its interfaced identifiers are

"imported" into the local scope.)

Now what happened here becomes clear. The scope of the variables Top, Min, and K, as well as that of the dummy parameter L, is limited to the procedure Sort. Once you exit Sort, those identifiers are no longer visible, and the Watch window reflects that.

There is an important qualification to the scope explanation. Consider the following sample program:

{$D+,L+}

program TestScopei

var

W,X,Y,Z : integer;

procedure Swap(var X,Y integerl;

var

Z : integer;

begin Z := X;

X := Y;

Y := Z

end; { of proe Swap I

begin { main body of TestSeope W := 10; X := 20; Y := 30; Z := 40;

Swap(X,Wl;

Swap(Y,Xl;

Swap(Z,Yl;

Writeln(W:8,X:8,Y:8,Z:8) end. { of program TestSeope }

Type in this program, save it as SCOPE.PAS, compile it using Compile/

Make (F9), then press F7. Once the Watch window appears, add W, X, Y, and 2 to it with Break/Watch/Add Watch (Ctrl-F7). Press F7 twice more; the execution bar should now be on the call to Swap (X, W), and the Watch window should look like this:

• W: 10 X: 20 Y: 30 Z: 40

Now press F7 once more, so that you've traced into Swap. The Watch window now reads like this (2 is uninitialized, so its actual value will vary):

• W: 10 X: 10 Y: 20 Z: 21326

Step through until you reach the closing end of Swap. The Watch window will now read

• W: 20 X: 20 Y: 10 Z: 10

One more press of F7 returns you to the main body of TestScope, and the Watch window contains

• W: 20 X: 10 Y: 30 Z: 40

What's going on here? The values of W, X, Y,and Z appear to be varying widely for no reason at all. And yet it makes perfect sense once you understand this scope rule: When two identifiers (from different scopes) are identical, the "most local," or nested, identifier takes precedence.

Case in point: The procedure Swap redec1ares three identifiers X, Yand Z.

When you are stepping through the main body of TestScope, then the Watch window uses the global versions of X, Y, and Z. However, once you trace into Swap, the versions of X, Y, and Z local to Swap take precedence, and their values are displayed instead. Once you exit Swap, the global versions are used again.

Is there some way to help sort out this confusion? One way, of course, is to avoid using local identifiers that redec1are global identifiers. For example, you could rewrite Swap to look like this:

procedure Swap (var A,B: integer);

var

T : integer;

begin T := A;

A := B;

B := T

end; { of proe Swap }

Now, if you trace through, the Watch window correctly displays the values of the global versions of X, Y, and Z all the way through, since there are no local versions in Swap.

There are times, though, when renaming isn't feasible or desirable. Can you somehow avoid the confusion anyway? Suppose you have the original version of TestScope, but you want to trace the global variables all the way through. Can it be done?

Yes, by qualifying the identifiers. You qualify a global identifier by explicitly preceding it with the program or unit name. Here's the format:

module. identifier

where module is the program or unit name, and identifier is the identifier name. For example, you might set up the Watch window this way:

• X:

Y:

Z:

TestScope.W:

TestScope.X:

TestScope.Y:

TestScope.Z:

This lets you trace all the variables and dummy parameters throughout the entire program. In fact, you could go one step farther and make the Watch window look like this:

• x:

@X=@TestScope.X:

Y:

@Y=@TestScope.Y:

Z:

@Z=@TestScope.Z:

TestScope.W:

TestScope.X:

TestScope.Y:

TestScope.Z:

The first expression you added compares the address of X with the address of TestScope.X. When this expression is True, then you know that X refers to the global X; when it's False, then you know that X is some local variable or parameter. The other two expressions perform the same function for Y and Z.

You can also qualify local identifiers using a special syntax that specifies the full "procedural path" of that name. In the program TestScope, you could watch Swap's X parameter by watching TestScope.Swap.X.

Im Dokument Turbo Pascal® (Seite 139-142)