Dynamic Arrays in Types

Written by rdc

 Introduction 

A dynamic array in a type definition is a very useful feature, but FreeBASIC
 did not support it before. Or rather, it did not support it directly 
before. However, you could 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)
   myType.darray = Reallocate(myType.darray, 10 * SizeOf(Integer)) ' Editors Note: above code line changed to this
   '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.darray = Reallocate(myType.darray, 10 * SizeOf(Integer)) ' Editors Note: above code line changed to this

   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.

 Dynamic arrays fields as non-static members are now supported inside UDT 

Previous example but transposed, by using a dynamic array field as 
non-static member inside the UDT (feature now supported):
   'Define type:
   '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

Last reviewed by sancho3 on February 08, 2018  Notes: Error in code fixed 
and noted