Dynamic Arrays in Types
 
Written by rdc

Introduction


A dynamic array in a type definition is a very useful feature, but FreeBasic doesn't support it before version 1.00.0. Or rather, it doesn't support it directly before that version. However, you can create dynamic arrays by using pointers and the associated memory functions.

An array is simply a contiguous block of memory that holds a certain data type. Arrays in FreeBasic use an array descriptor to describe the data contained within the array, and you can use this same technique to build a dynamic array within a type. The two elements you need within your type-def are a pointer to a particular data type, and a size indicator.

You can then use the ptr field to allocate a block of memory to the needed size, and save that size in the size indicator field. The size field is used to tell you how many elements are currently in the array. Once the array has been initialized, you can then use pointer indexing to access each element in the array.

Getting the Point(er) in Code


The following program illustrates the steps in creating, initializing and resizing a dynamic type-def array.
'Define type:
'size is current size of array
'darray will contain array data
Type DType
    size As Integer
    darray As Integer Ptr
End Type

'Create an instance of type
Dim myType As DType
Dim As Integer i, tmp

'Create enough space for elements
myType.darray = CAllocate(5, SizeOf(Integer))
'Set the length of the array
'in the array size indicator
myType.size = 5

'Load data into array
For i = 0 To myType.Size - 1 
    myType.darray[i] = i
Next

'Print data
For i = 0 To myType.Size - 1
    Print "darray[";i;" ]:";myType.darray[i]
Next
Print "Press any key..."
Sleep
Print

'Save the current array size
tmp = myType.size
'Now resize the array
myType.darray = Reallocate(myType.darray, 10)
'Set the length indicator
myType.size = 10

'Load in data into new allocation
For i = tmp To myType.Size - 1
    myType.darray[i] = i
Next

'Print out contents
For i = 0 To myType.Size - 1
    Print "darray[";i;" ]:";myType.darray[i]
Next
Print "Press any key..."
Sleep

'Free allocated space
Deallocate myType.darray

End


How it Works


The first step is, of course, to define the type-def:
Type DType
    size As Integer
    darray As Integer Ptr
End Type

Since this is just an example there are only two elements within the type, a size indicator and the array pointer. Notice that the array pointer is defined as an Integer ptr. When you define a pointer to a particular type, you are creating a "typed" pointer. The compiler can use this type information to check to make sure the values being placed into the array are valid, and will also use this information for pointer arithmetic.

The next step is to define the working variables.
Dim myType As DType
Dim As Integer i, tmp

Here an instance of the type is created, as well as some working variables that are used in the following code. WARNING: You must initialize the array pointer before you can use it; using an uninitialized ptr can cause program crashes, system lockups and all sorts of bad things.
myType.darray = CAllocate(5, SizeOf(Integer))
myType.size = 5

These two lines of code initialize the array pointer to hold 5 integers. Callocate is used to allocate the memory segment, since Callocate will initialize the segment to zeros.

The size field stores the current length of the array. Now, of course, you could calculate the size of the array by simply dividing the number of bytes in the allocation by the size of an integer, but using a size indicator within the type is much cleaner and saves you a calculation in your program.

For i = 0 To myType.Size - 1 
    myType.darray[i] = i
Next

This section of code loads the array with some values. You can see why saving the size of the array simplifies the coding process. Since the array is a typed pointer, you can access the array using the pointer indexing method, which is almost like accessing a predefined array.

For i = 0 To myType.Size - 1
    Print "darray[";i;" ]:";myType.darray[i]
Next

This section simply prints out the values using the same method that was used to load the array.

Of course, this should be a dynamic array, so you should be able to resize the array, and this is exactly what the next section of code will do.
tmp = myType.size
myType.darray = Reallocate(myType.darray, 10)
myType.size = 10

The first line of code saves the current size of the array so that the new memory segment can be initialized while not overwriting any existing data. You will see this in a moment.

The second line uses the Reallocate function to resize the memory segment, that is, resize the array. In this case, the array is being made larger; you could of course make the array smaller. If you were to make the array smaller, any data not in the new segment would be lost, as you would expect.

The last line of code above saves the new array size in the size indicator.

For i = tmp To myType.Size - 1
    myType.darray[i] = i
Next

Here, you can see why the old array size was saved. In the For statement, the initialization procedure iterates through the newly added indexes, storing data within the memory segment. This is like using the Redim Preserve statement on a normal array.

For i = 0 To myType.Size - 1
    Print "darray[";i;" ]:";myType.darray[i]
Next

This code section simply prints out the new values.

Deallocate myType.darray

This is vitally important. You should always deallocate any allocated memory that you have created in your program to prevent memory leaks.

When you run the program you should see the following output:
darray[ 0 ]: 0
darray[ 1 ]: 1
darray[ 2 ]: 2
darray[ 3 ]: 3
darray[ 4 ]: 4
Press Any key...

darray[ 0 ]: 0
darray[ 1 ]: 1
darray[ 2 ]: 2
darray[ 3 ]: 3
darray[ 4 ]: 4
darray[ 5 ]: 5
darray[ 6 ]: 6
darray[ 7 ]: 7
darray[ 8 ]: 8
darray[ 9 ]: 9
Press Any key...

The first print out shows the original array. The second print out shows the newly resized array.



From fbc version 1.00.0, dynamic arrays fields as non-static members are supported inside UDT


Previous example transposed for fbc version 1.00.0 or greater, by using a dynamic array field as non-static member inside the UDT (feature now supported):
'Define type (for fbc version >= 1.00.0):
'darray will contain array data
Type DType
    darray(Any) As Integer
End Type

'Create an instance of type
Dim myType As DType
Dim As Integer i, tmp

'Create enough space for elements
ReDim myType.darray(4)

'Load data into array
For i = 0 To UBound(myType.darray)
    myType.darray(i) = i
Next

'Print data
For i = 0 To UBound(myType.darray)
    Print "darray(";i;" ):"; myType.darray(i)
Next
Print "Press any key..."
Sleep
Print

'Save the current array upper bound
tmp = UBound(myType.darray)
'Now resize the array
ReDim Preserve myType.darray(10)

'Load in data into new allocation
For i = tmp + 1 To UBound(myType.darray)
    myType.darray(i) = i
Next

'Print out contents
For i = 0 To UBound(myType.darray)
    Print "darray(";i;" ):";myType.darray(i)
Next
Print "Press any key..."

Sleep