Previous | Table of Contents | Next |
5.2.8.1. Using and Accessing Pointer-Based Structures
In the sense of dynamic memory allocation, a predescribed data type can be created, destroyed, moved, or changed by use of assignments, or procedures created for dynamic allocation, such as new() and dispose().
new() creates or assigns a position in the heap for a variable type specified in the data type for that variable, and dispose() removes the position from the heap previously allocated with new(). The designation in a program that indicates a variable is a pointer is the caret symbol (^) as part of the type or variable name. For example, observe the methods by which the simpler structures in Listing 5.31 are created and accessed. Note that this is a very inefficient way, memory wise, to use pointers (they are usually used in more complex variable types).
This listing also illustrates the difference between readdressing a pointer and modifying the contents in heap memory of that pointer. The pointer variable itself, without the caret, represents a mostly static (but sometimes dynamic) position of memory that holds a memory address to a spot on the heap, and the pointer variable (including the caret) represents the value in the memory position in the heap.
As you may expect, a pointer cannot point to a particular address in memory at all. To accomplish this, the pointer variable itself is assigned the value represented by the reserve word nil (pvar := nil;). In Turbo Pascal, this represents making the pointer point to nothing.
Listing 5.31. A rudimentary demonstration of pointer-based structures.
program fig31; type inttype = ^integer; { note caret required, read pointer to an integer } var a, b: inttype; begin new(a); { note, no ^ on new or dispose as they address pointers } new(b); writeln(Largest Block Available After New: , maxavail); writeln(Total Memory Available After New: , memavail); a^ := 2; b^ := 5; writeln(a^, , b^); { note that pointers are static structures which represent ADDRESSES, and not the INTEGER themselves. So to move the pointer, and eliminate the problem illustrated in Tips/Traps, dispose must be called BEFORE a POINTER itself is readdressed. } dispose(a); a := b; { a now points at the heap memory b points to } writeln(a^, , b^); { note now this write puts out 5 on both cases } dispose(b); { to clean up in the program. Note either dispose(a) or dispose(b) may be called here, since dispose operates on the areaeserved in heap memory and not the pointer, but good practice is to match up new() calls with dispose() calls } writeln(Largest Block Available After Dispose: , maxavail); writeln(Total Memory Available After Dispose: , memavail); end.
With the dynamic nature of memory allocation, a means is needed to determine how much memory is left to allocate. In this case, the variables memavail and maxavail and the function sizeof() are available. As memory can get fragmented, maxavail represents the largest continuous block of memory available to be allocated. memavail represents the total amount of memory free to be allocated. sizeof(var) returns the physical number of bytes occupied by that variable in memory.
5.2.8.2. Address of Items in Memory
In addition to pointer addresses, which may be used as indicated in the preceding section or as linked into lists or trees (one static pointer holds a record system that is allocated in the heap with pointers in that record system which are allocated for more space on the heap), an address operator may be used to refer to addresses of procedures, functions, or static variables in memory. It also can be used to aid in changing items in the program, like dynamic procedures and functions, as well as setting pointers to values returned by functions and procedures (internally, that is what is happening when procedures and functions are used). (If you are interested in the types of structures described here, locate a data structures book.) The @ symbol, or in Pascal, read as the address of, is used in this case to represent the memory address of a variable, procedure, or function.
An example of doing this is shown in Listing 5.32. Far procedures must be used as illustrated to make something like this routine work. For example, a sort routine with options to sort by multiple functions would be ideal for this kind of coding, though this case would be ludicrous. Note that a pointer is also addressed in this listing to a static variable position in memory.
Listing 5.32. Demonstration of the @ operator.
program fig32; type intptr = ^integer; proctype = procedure(a: string); var c: integer; d: intptr; callit: proctype; {$F+} { this can be used or far; after each procedure } procedure yesitisafive(a: string); begin writeln(D is a 5.); writeln(a); end; procedure noitisnotafive(a: string); begin writeln(D is not a 5.); writeln(a); end; {$F-} begin @callit := nil; { requirement to make it work } c := 5; d := @c; writeln(Pointer contents of d are , d^); if d^ = 5 then @callit := @yesitisafive else @callit := @noitisnotafive; callit(Whatever works is fine.); end.
Previous | Table of Contents | Next |