Chapter 6

Using data structures

User-defined TYPE structures and UNIONs are alternatives to using arrays, a method closer to C language, and PASCAL. This might be important if you are making versions of your source code in different languages. Or, if you are a C or PASCAL programmer now trying to do versions in PowerBASIC.

They are a good way to make general purpose programs, with external $include files, to handle specific data formats. They make it easier to use random files. They give you another kind of "reusable software part". They help make PowerBASIC a user-extendible, customizable language.

TYPEs are a way to label groups of data variables, with a single label as a unit, in effect, a table of pointers to data storage locations in memory, with instructions for each item in the table, a list accessed by a single label.

This can save you time by letting you think of processing big sets of data, instead of each piece of data. They are another form of "reusable software parts", like subroutines and functions.

UNIONs are similar to TYPE structures. You can have more than one label for the same data, in effect, more than one pointer to the same storage locations in memory, like an alias.

You might want to use UNIONs, because sometimes a piece of data might be integer and sometimes it might be a string, when a space on a form has more than one kind of answer. Or you might want to refer to a piece of data byte by byte, or as a whole string.

Whereas TYPE differentiates and subdivides, making one record label equal to a list of labels of individual pieces of data, UNIONs say that two things are the same and unifies, making a list of labels all point to the same data storage locations in memory.

The TYPE structure defines a list of pre-defined formatting instructions, so that PowerBASIC will know how to deal with each kind of data in the group.

Instead of thinking in terms of single values, you can think in terms of records, like a whole page, instead of one part of one line on that page.

Because TYPES tell PowerBASIC how to process a whole group of data elements, they are, in a sense, a data handling macro, a custom recipe, a recipe for setting aside memory.

Once defined, you can use them the same way as built-in standard simple data TYPEs, making PowerBASIC a user-extendible, customizable language.

You can place the TYPE or UNION definitions in an $include file, and use them over and over again, just asif they had been added to PowerBASIC.

User-defined TYPE structures can be associated with your random file's buffer.

One advantage to their use is the data labels are more readable months later, or when another person takes over your source code. This is important if you are selling source code toolkits, or you have a lot of projects.

Another advantageous use would be where you might have hard-coded data formatting instructions for a certain kind of data record, and you want to make the formatting details an external $include file, that can be different for different software products. If you sell source code toolkits, or compiled libraries, as add-ons, your users could more easily create programs with different data formats.

Fixed vs Flexible-Length

TYPE structures and UNIONs are fixed-length, complex data TYPEs, constructed at compile time.

PowerBASIC also provides a mechanism for flexible-length strings, to define variable-length data structures, dynamically at runtime. Flex strings are introduced in Chapter *******, and discussed in more detail later in this chapter.

See PowerBASIC Reference Guide, TYPE, and UNION.

See PowerBASIC Reference Guide, Table 2.5, on page 116, for the built-in standard kinds of simple data TYPE.

What are user-defined TYPE structures?

The difference between arrays and user-defined TYPE structures, i.e., user-defined records, is similar to the difference between rows and columns on the monitor screen.

Instead of defining each variable array separately, like a vertical stack of elements all the same TYPE, and then using subscripts, when you define a user-defined TYPE structure, it is like a horizontal layer made up of one each out of all the vertical stacks. The record structure is a mixture of simple data TYPEs.

Imagine that you have a column of integers, a column of text strings, and a column of double-precision numbers. A "user-defined TYPE structure", or custom record, would be like one horizontal row across, with one integer, one textstring, and one double-precision number.

Each record of that TYPE structure has one variable from each vertical array.

Another analogy is instead of putting all the apples in one bushel, and all the oranges in another bushel, and all the nectarines in another bushel, we take one apple, one orange, and one nectarine, and put these in a small basket, and we make 20 baskets just like this.

It is a different way of referring to the same storage locations in memory.

How we define TYPEs and UNIONs

To tell the compiler to create a user-defined TYPE, start with a TYPE command, and a unique name for the new complex data TYPE, and end with END TYPE. The lines in between these two define the field names and their simple (or even complex) data TYPEs and fixed-length sizes.

TYPE NameOfNewComplexDataType TYPE is a reserved word.

...

...

...

...

...

END TYPE END TYPE is a key word.

Example

TYPE FruitBasket

Apple AS STRING *12

Orange AS STRING *12

Nectarine AS STRING *12

END TYPE

DIM ChristmasPresent(1:20) AS FruitBasket

A real recipe, as a further analogy:

TYPE Oatmeal

RolledOats AS STRING *12

Water AS STRING *12

Raisins AS STRING *12

BrownSugar AS STRING *12

Milk AS STRING *12

END TYPE

DIM Breakfast AS Oatmeal

Example

If you have six pieces of data for each person, and ten people on your donors list, you could refer to them in two different ways:

Two ways to dimension them

The array method

dim PersonsName$(10),Address$(10),CityStateZip$(10)

dim Phone$(10),Salary(10),Donated(10)

The user-defined TYPE method

TYPE DonorEntry

PersonsName AS STRING * 24

Address AS STRING * 24

CityStateZip AS STRING * 24

Phone AS STRING * 12

Salary AS LONG

Donated AS LONG

END TYPE

DIM DonorData(1:10) AS DonorEntry

Remarks

The TYPEs of data can be only fixed-length, not variable-length.

Two ways to refer to individual fields

The array method

PersonsName$(1)="John Doe"

Donated(1)=200.00

The user-defined TYPE method

DonorData.PersonsName(1)="John Doe"

DonorData.Donated(1)=200.00

----------------------------------------------------------------------------

The array method

TotalDonated=0

for i=1 to imaxx%

print "PersonsName = ";PersonsName$(i),"Donated = ";Donated(i)

TotalDonated=TotalDonated+Donated(i)

next i

print"Total Donated is ";TotalDonated

The user-defined TYPE method

TotalDonated=0

for i=1 to imaxx%

print "PersonsName = ";DonorData.PersonsName(i);" ";

print "Donated = ";DonorData.Donated(i)

TotalDonated=TotalDonated+DonorData.Donated(i)

next i

print"Total Donated is ";TotalDonated

Remarks

The variable labels are longer, but they will save time in program maintenance, in the long run.

-------------------------------------------------------------------------

Another viewpoint is to take a multidimensional array, with two or more subscripts, and show it done two ways.

DIM Expenses(5,12)

In the array method, all of these data elements must have the same TYPE.

In the user-defined TYPE structure method, you can have several kinds of data TYPE.

TYPE ExpenseEntry

Description AS STRING * 32

VendorCode AS STRING * 6

Category AS STRING * 2

Date AS STRING * 8

Amount AS LONG

END TYPE

DIM ExpenseData(10,12,3) AS ExpenseEntry

Nesting user-defined TYPEs

The multi-level labeling of nested user-defined TYPE structures is parallel to subdirectory pathnames. Instead of a backslash, use a period to separate the different levels of label.

Instead of a "subdirectory" of disk blocks, you have a "subdirectory" of data cell locations, showing the hierarchy of how the pieces of data are related. It is a "data trail", or record of the path thru the levels of data.

Instead of storing the students' names as two separate fields,

StudentLastName$(i), StudentFirstName$(i)

we could instead define a TYPE structure called "NameRec".

TYPE NameRec

Last AS STRING * 20

First AS STRING * 15

Initial AS STRING * 1

END TYPE

TYPE HomeAddress

StreetName AS STRING * 28

HouseNumber AS STRING * 6

City AS STRING * 24

State AS STRING * 20

Zip AS STRING * 10

END TYPE

TYPE TestRecord

WeeklyTest AS INTEGER

MonthlyTest AS INTEGER

SemesterExam AS INTEGER

END TYPE

TYPE GradesEntry

CourseName AS STRING * 30

Dates AS STRING * 8

Teacher AS STRING * 24

GradeEarned AS TestRecord

END TYPE

TYPE PupilRecord

FullName AS NameRec

IDnum AS STRING * 12

Address AS HomeAddress

Phone AS STRING * 12

Transcript AS GradesEntry

END TYPE

DIM StudentRecord(1:30) AS PupilRecord

StudentRecord.Transcript.GradeEarned.WeeklyTest

---------------------------------------------------------------------------

In programs with this TYPE of labeling, you should probably not use periods in variable names in other ways, to prevent confusion.

You will probably not want to go more than two or three levels. You need to keep your names short, so that when you have nested TYPE structures, your variable names will not become too long.

The practical limit of using these nested TYPE structures is the length of the label used to access a variable, up to 255 characters maximum.

Using arrays of user-defined TYPE structures

Arrays of user-defined TYPE structures are even possible!

Just DIM StudentRecord(1:30) AS PupilRecord

then, when you want to refer to a particular field within that record, it would look like this:

for i=1 to ClassSize

PRINT StudentRecord.FullName.Last(i)

next i

Using user-defined TYPEs with

procedures and functions

User-defined TYPE structures work inside procedures and functions,

just fine.

def FNPrintLastNameList

for i=1 to ClassSize

PRINT StudentRecord.FullName.Last(i)

next i

end def

-----------------------------------------------------------------------------

SUB PrintLastNameList( StudentRecord.FullName.Last(i), ClassSize)

for i=1 to ClassSize

PRINT StudentRecord.FullName.Last(i)

next i

end sub

Use with a random disk file's buffer

Instead of hard-coded data formatting instructions for a certain kind of data record, you might want to make the formatting details an external $include file, that can be different for different software products.

This is especially useful in working with random disk files. See chapter 9, Files, page 219, for more information.

**********************************p 163

Storage requirements

To find out how much memory to allocate for a certain user-defined TYPE structure, use the LEN function with the name of the variable in memory, not the name of the TYPE structure.

Obtaining addresses with VARPTR

The address returned by VARPTR (DataRecord) is the address of the first byte of data in that record.

Example

StudentProfile=VARPTR(StudentRecord)

Using VARPTR with the full "data trail" label, like a subdirectory name,

will return the starting byte of that individual field in the record.

Example

TestScore=VARPTR(StudentRecord.Transcript.GradeEarned.WeeklyTest)

UNIONS

In UNIONs, you can have more than one label for the same data location.

It is like declaring an "alias".

UNIONs are particularly helpful in dealing with data in memory as 16-bit

words, 32-bit words, or 8-bit bytes.

This file has been created with the evaluation or unregistered copy of
EasyHelp from Eon Solutions
(Tel: UK 0973 209667, Email: eon@cix.compulink.co.uk)