PureBasic

Структуры


Введение

В программировании часто возникает необходимость описать объекты, которые нельзя охарактеризовать только одной числовой или строковой величиной. Использование обычных переменных в этих случаях не всегда удобно.

Например, представим себе, что нам нужно программно обработать информацию о студентах. Данные о каждом студенте включают в себя величины нескольких типов – строку с именем и инициалами, целое число, показывающее возраст студента в годах, средний балл, являющийся вещественным значением, и т.д. Простейший путь решения задачи – описать в программе набор соответствующих переменных:

Define name.s    ;  Фамилия
Define age.a    ;  Возраст в годах (любому студенту для возраста хватит и 255 лет :))
Define ball.f    ;  Средний балл

Но, когда студентов станет много, нам придётся перейти от переменных к массивам:

Dim name.s(10)  ;  Фамилия
Dim age.a(10)  ;  Возраст в годах
Dim ball.f(10)  ;  Средний балл

Чтобы извлечь информацию о 9-м студенте, нам нужно обработать величины name.s(9), age.a(9) и ball.f(9). Таким образом, информация об одном объекте оказывается "раскидана" по нескольким контейнерам-массивам, что делает крайне неудобными операции со "студентом" как с единым объектом. Преодолеть это неудобство можно с помощью специальных типов данных, которые в Purebasic и C/С++ называются структурами.

Структура – это определяемый пользователем составной тип данных, представляющий собой упорядоченное в памяти множество элементов различного типа. В отличие от простых типов (Float, Long...), он позволяет объединять в одном объекте значения, которые могут иметь разные типы данных. Для использования структур в программе необходимо выполнить три действия:

1.    Создать шаблон структуры, т.е. определить новый тип данных, который впоследствии можно использовать для создания экземпляров структуры с этим типом.

2.    Создать экземпляр структуры . Этот этап подразумевает инициализацию конкретной переменной с заранее определенной (с помощью шаблона) внутренней структурой.

3.    Организовать обращение к элементам экземпляра структуры, которые обычно называют "полями" структуры.


Для создания шаблона структуры в Purebasic есть ключевое слово Structure.

Синтаксис
Structure <name> [Extends <name_ext>] [Align <numeric expression>]
  ...
EndStructure 

Параметры

<name> Это имя шаблона структуры, т.е. название создаваемого пользователем типа данных. Это имя будет использоваться для инициализации переменных, точно так же, как используется Float, Integer и тд.
Extends (дополн.) Этот параметр даёт возможность расширять шаблон структуры новыми полями, из ранее созданного шаблона структуры с именем <name_ext>. Все поля, найденные в шаблоне <name_ext>, будут добавлены перед полями шаблона <name>.
<name_ext> (дополн.) Имя ранее созданного шаблона структуры, который будет использован для расширения создаваемого нового шаблона структуры.
Align (дополн.) Этот параметр позволяет принудительно задать выравнивание в памяти полей структуры, по адресам кратным величине <numeric expression>.
<numeric expression> (дополн.) Величина характеризующая кратность адреса, до которого будет дополняться поле структуры.

Описание



Как пример, структура 'Student', систематизирующая данные о студенте, может быть описана следующим образом:

Structure Student ; Создаём шаблон структуры
    name.s          ; Фамилия
    age.a          ; Возраст в годах
    ball.f          ; Средний балл
EndStructure

Define Professor.Student\name = "Maria_Ivanna" ; Объявляем экземпляр структуры 'Student', в виде переменной 'Professor' с типом 'Student'.
Professor\age = 44
Professor\ball = 50

Dim Pupils.Student(10) ; Объявляем массив структур 'Student', в виде массива Pupils() с типом 'Student'.

Pupils(0)\name = "Stepan" ; Заполняем данные первого студента.
Pupils(0)\age = 20          ;
Pupils(0)\ball = 84          ;
Pupils(1)\name = "Sveta"  ; Заполняем данные второго студента.
Pupils(1)\age = 19          ;
Pupils(1)\ball = 81          ; Так можно заполнить каждый элемент массива…

Debug "Имя профессора: " + Professor\name ; Выводим информацию из поля "\name"
For q=0 To 1
    Debug "Имя:      " + Pupils(q)\name
    Debug "Возраст:  " + Pupils(q)\age
    Debug "Баллы:    " + Pupils(q)\ball
Next q
End


В строках 1-5 создаётся шаблон структуры. Операторы Structure и EndStructure определяют начало и конец шаблона структуры. Между ними расположены переменные 'name.s', 'age.a' и 'ball.f', которые будут входить в состав структуры в качестве элементов (полей). В данном случае, эти переменные типов String, ASCII и Float, но могут быть любых.
Структуре присвоено имя 'Student', которое будет использоваться в качестве типа (как Float, long и тд.) при создании экземпляров структуры.

В строке 6 происходит создание экземпляра структуры, то есть инициализация переменной Professor с типом 'Student'. Доступ к полям экземпляра структуры происходит с помощью символа "\" и имени поля. Таким образом, в этой же строке мы записываем имя профессора в поле "\name".
В строках 7 и 8 заполняем остальные данные. Как и в переменной обычного типа, здесь тип уже можно не указывать.

В строке 9 происходит создание целого массива экземпляров структуры 'Student', то есть инициализация массива Pupils() с типом 'Student'. Так как при инициализации использован структурный тип, то каждый элемент массива Pupils() становится экземпляром структуры, это значит, что каждый элемент будет содержать по набору полей, как определено в шаблоне структуры 'Student', и для каждого элемента будет выделена память с учётом всех входящих в данный элемент полей. Такие массивы также называют - Структурированными массивами. Таким же путём получают Структурированные Связные списки и Структурированные Хеш-карты (Ассоциативные массивы).

Для создания экземпляров структуры, может быть использован любой объект, которому при инициализации можно указать тип. Это Переменная, Массив, Связный список и Хеш-карта.

В качестве членов (полей) Структуры можно использовать в любой комбинации, следующие объекты:

1. Переменные любого типа (float, integer, string и тд.), в том числе и типа определённого пользователем, т.е. другой ранее определённой структуры (не этой). Это означает, что в качестве члена структуры, можно использовать другую структуру.

2. Динамические объекты, такие как Массивы, Связные списки и Хеш-карты. Они будут автоматически инициализированы при создании экземпляра структуры. Чтобы объявить их при создании шаблона структуры, используйте следующие ключевые слова: Array, List и Map. Они также могут быть любого типа, в том числе и типа определённого пользователем, а Связные списки и Хеш-карты - даже типа этой же структуры. Так как в отличие от массива изначально не содержат элементов, и следовательно не содержат экземпляров этой структуры.

3. Статические массивы. Эти массивы объявляются без ключевого слова Array, а размерность указывается в квадратных скобках [ ]. Они также могут быть любого типа, кроме типа этой структуры. Учтите, что в структурах статический массив ведёт себя не так, как обычный динамический BASIC-массив (объявляемый с помощью ключевого слова Array), чтобы соответствовать формату структур C/C++ (для обеспечения прямого переноса структур API). Это означает, что команда arr.i [2] выделит массив из 2-х элементов (от 0 до 1), в то время как команда Array arr.i (2) выделит массив из 3-х элементов (от 0 до 2).

4. Указатели Это переменные специального типа, которые могут хранить только адрес объекта в памяти. В шаблоне структуры объявляются с символом '*' перед именем указателя (например - *Next), но при организации доступа к полю указателя, символ '*' не ставится (просто - \Next), чтобы соответствовать формату структур C/C++ (для обеспечения прямого переноса структур API). Если нужен доступ через указатель к полям экземпляра структуры, этот указатель должен быть объявлен с именем шаблона структуры в качестве типа. Например, если нужен доступ к полям структуры 'Timo.Person', указатель *Next должен быть объявлен как: '*Next.Person'.

5. Структурное объединение. Это объединение нескольких элементов структуры, с предоставлением им одной области памяти на всех. См. ниже.

Пример: Использование указателя для хранения адреса конкретного экземпляра структуры.

Structure Person ; Создаём шаблон структуры
    Name$
    Age.b
EndStructure

Timo.Person\Name$ = "Timo" ; объявляем экземпляр структуры Timo.Person и сразу записываем имя в переменную Name$
Timo\Age = 25
Fred.Person\Name$ = "Fred"
Fred\Age = 25

*Next.Person = @Fred    ; Объявляем указатель с типом структуры, и присваиваем адрес структуры Fred.Person.
Debug *Next.Person\Name$ ; Отобразит 'Fred'
*Next.Person = @Timo    ; Теперь присваиваем адрес структуры Timo.Person.
Debug *Next.Person\Name$ ; Отобразит 'Timo'

Пример: Использование указателя в качестве элемента структуры.

Structure Person  ; Создаём шаблон структуры
    *Next.Person  ; Указатель объявляется с типом структуры. Здесь символ '*' обязателен.
    Name$
    Age.b
EndStructure
Timo.Person\Name$ = "Timo"
Timo\Age = 25

Fred.Person\Name$ = "Fred"
Fred\Age = 25

Timo\Next = @Fred    ; Присваиваем адрес Fred.Person. При доступе к полю Next (указателю), символ '*' должен быть опущен.
Debug Timo\Next\Name$ ; Отобразит 'Fred', так как указателю присвоен адрес структуры Fred.Person
Timo\Next = @Timo      ; Теперь присваиваем указателю адрес Timo.Person.
Debug Timo\Next\Name$ ; Отобразит 'Timo', так как указателю присвоен адрес структуры Timo.Person

Пример: Комбинируем разные типы элементов в структуре (обратите внимание на разные типы массивов).

Structure Window      ; Создаём шаблон структуры Window
    *NextWindow.Window ; Указатель с типом Window, для доступа к экземплярам структуры Window
    x.w                  ; Переменная с типом Word
    y.w
    Name.s[10]          ; Статический массив с типом String, 10 элементов в запасе (от 0 до 9)
    Array  Age.l(10)    ; Динамический массив с типом Long, 11 элементов в запасе (от 0 до 10)
EndStructure

Пример: Статический, динамический массив и передача структуры в процедуру

Structure Whatever
    a.l
    b.l[2]          ; Статический массив не изменяемого размера (стандарт C) с 2 значениями b[0] и b[1].
    Array c.l(3, 3)    ; Динамический массив с 16 значениями от c (0,0) до c (3,3), изменяемый с помощью ReDim()
EndStructure

MyVar.Whatever

Procedure MyProcedure(*blahblah.Whatever)
    *blahblah\a = 5
    *blahblah\b[0] = 1
    *blahblah\b[1] = 2
    *blahblah\c(3, 3) = 33
EndProcedure

MyProcedure(@MyVar)
Debug MyVar\a
Debug MyVar\b[0]
Debug MyVar\b[1]
Debug MyVar\c(3, 3)

;Debug MyVar\c(0, 10) ; Ошибка индекс за пределами границ
ReDim MyVar\c(3, 10) ; Остерегайтесь, только последнее измерение может быть изменено!
Debug  MyVar\c(0, 10) ; Круто, массив теперь больше!

Пример: Вложенная структура(ы)

Structure pointF
    x.f
    y.f
EndStructure

Structure Field
    Field1.q
    Field2.s{6}
    Field3.s
    Array Tab.pointF(3)
EndStructure

Define MyVar.Field

MyVar\Tab(3)\x = 34.67

Пример: Использование Связного списка в качестве элемента структуры.

Structure Person ; создаём шаблон структуры Person
    Name$
    Age.l
    List Friends$()
EndStructure

John.Person          ; Создаём экземпляр структуры в виде переменной John с типом Person
John\Name$ = "John"    ; В переменную Name$ пишем имя "John"
John\Age  = 23        ; В переменную Age пишем его возраст

AddElement(John\Friends$()) ; в Связный список Friends$(), добавляем Джону друзей…
John\Friends$() = "Jim"
AddElement(John\Friends$())
John\Friends$() = "Monica"

ForEach John\Friends$()
    Debug John\Friends$() ; вывести всех друзей Джона
Next

Пример: Расширение шаблона структуры полями из ранее созданного шаблона структуры.

Structure MyPoint  ; Создаём шаблон структуры MyPoint
    x.l
    y.l
EndStructure

Structure MyColoredPoint Extends MyPoint ; Создаём шаблон структуры MyColoredPoint и добавляем поля из шаблона MyPoint
    color.l
EndStructure

ColoredPoint.MyColoredPoint\x = 10  ; Создаём экземпляр структуры в виде переменной ColoredPoint с типом MyColoredPoint
ColoredPoint\y = 20                    ; Пробуем доступ к дополнительным полям 'x.l' и 'y.l'
ColoredPoint\color = RGB(255, 0, 0)
Debug ColoredPoint\y ; и убеждаемся, что доступ есть.

Можно выполнить копирование данных из одного экземпляра структуры в другой экземпляр той же структуры, используя между ними знак равенства.

Пример: Копирование данных экземпляра структуры.

Structure MyPoint  ; создаём шаблон структуры MyPoint
    x.l
    y.l
EndStructure

LeftPoint.MyPoint\x = 10 ; Создаём экземпляр структуры, в виде переменной LeftPoint с типом 'MyPoint'
LeftPoint\y = 20
RightPoint.MyPoint = LeftPoint ; Создаём ещё один экземпляр той же структуры, и копируем в неё данные из LeftPoint

Debug RightPoint\x
Debug RightPoint\y

Пример: Создание некого подобия дерева из экземпляров структуры.

Structure x ; Создаём шаблон структуры
    List y.x()
EndStructure

x.x ; Объявляем переменную X с типом X
AddElement(x\y())
AddElement(x\y()\y())
AddElement(x\y()\y()\y())
AddElement(x\y()\y()\y()\y())
AddElement(x\y()\y()\y()\y()\y())
AddElement(x\y()\y()\y()\y()\y()\y())
AddElement(x\y()\y()\y()\y()\y()\y()\y())
AddElement(x\y()\y()\y()\y()\y()\y()\y()\y())


Предопределённые шаблоны структур.

В Purebasic есть множество предопределённых шаблонов структур, которые сразу можно использовать в программе, просто ссылаясь на один из готовых шаблонов. Все их можно просмотреть в Среде разработки Purebasic (IDE) -> "Инструменты"->"Просмотр структур"-> "Имя шаблона структуры".
Поскольку многие из этих шаблонов основаны на структурах API OS, то при создании кроссплатформенного приложения использовать их нужно осторожно, так как на другой платформе/OS нужная структура может отличаться или отсутствовать вовсе.
При создании нового шаблона структуры нельзя использовать имена предопределённых шаблонов структур, а если создаваемое приложение должно быть кроссплатформенным, то и имена предопределённых шаблонов структур всех платформ и OS, на которых предполагается использовать Ваше приложение.
Если попытаться создать новый шаблон структуры с именем предопределённого шаблона структуры, компилятор выдаст ошибку "Строка ххх: Structure already declared: Имя структуры (in a resident file)".

Например, в Windows есть шаблон структуры 'Point'. Эта структура содержит переменные для координат точки на плоскости и представляет собой такую конструкцию:

Structure POINT
    x.l
    y.l
EndStructure

Это значит, что нельзя создавать шаблоны структуры с таким именем.
В то же время, поскольку шаблон структуры 'Point' уже есть в Purebasic на Windows, такой код на Windows будет работать без создания шаблона структуры:

Define Point1.Point, Point2.Point  ; Создаём 2 экземпляра структуры Point, в виде переменных Point1 и Point2 с типом Point
*CurrentPoint.Point = @Point1        ; Объявление указателя с типом структуры Point, и его инициализация с адресом Point1
*CurrentPoint\x = 10                ; Присваиваем значение 10 переменной Point1\x через указатель
*CurrentPoint.Point = @Point2        ; Переходим к адресу Point2
*CurrentPoint\x = 20                ; Присваиваем значение 20 переменной Point2\x через указатель
Debug Point1\x                        ; Отображаем переменную Point1\x
Debug Point2\x                        ; Отображаем переменную Point2\x

Выравнивание структуры.

Дополнительный параметр Align <numeric expression>, позволяет принудительно задать выравнивание в памяти полей структуры, по адресам кратным величине <numeric expression>. Значение по умолчанию равно 1, и означает отсутствие выравнивания. Например, если для выравнивания установлено значение 4, каждое поле будет выровнено по адресу кратному 4.
Это может повысить производительность при доступе к полям структуры, но при этом может использовать больше памяти, так как при выравнивании поля дополняются неиспользуемыми байтами. Если необходимо передать структуру с одной платформы на другую, то выравнивание может нарушить совместимость, в таком случае его использовать не следует.

Пример: Принудительное выравнивание полей структуры по адресам кратным 4 байтам.

Structure Type Align 4
    Byte.b
    Word.w
    Long.l
    Float.f
EndStructure

Debug OffsetOf(Type\Byte)  ; Отобразит 0
Debug OffsetOf(Type\Word)    ; Отобразит 4
Debug OffsetOf(Type\Long)    ; Отобразит 8
Debug OffsetOf(Type\Float)    ; Отобразит 12

Для выравнивания структуры по стандарту языка C, может быть использовано специальное значение #PB_Structure_AlignC, это может пригодиться при импорте структур языка C, для использования с функциями API. Если Вы используете значение #PB_Structure_AlignC, то для экономии памяти, имеет смысл правильно организовать заполнение структуры полями. К примеру:

Structure Test1 Align #PB_Structure_AlignC
    a.a  ; 1 bute  + 7 байтов добавка, потому что следующее поле 8-байтное и должно быть по адресу кратному 8.
    b.d    ; 8 bute  + 0 байтов добавка.
    c.a    ; 1 bute  + 3 байта добавка потому что следующее поле 4-байтное и должно быть по адресу кратному 4
    d.l    ; 4 bute  + 0 байтов добавка, так как следующее поле опять 4-байтное и расположено сразу за этим.
    e.l    ; 4 bute  + 4 байта добавка, потому что следующее поле 8-байтное и должно быть по адресу кратному 8.
    f.d    ; 8 bute  + 0
EndStructure

Structure Test2 Align #PB_Structure_AlignC
    f.d  ; 8 bute + 0 байтов добавка, потому что это поле стоит по адресу кратному 8 и следующее расположено сразу за этим.
    b.d    ; 8 bute + 0 байтов добавка, потому что следующее поле должно быть по адресу кратному 4, условие выполняется.
    d.l    ; 4 bute + 0 байтов добавка, так как следующее поле опять 4-байтное и расположено сразу за этим.
    a.l    ; 4 bute + 0 байтов добавка, так как следующее поле не требует выравнивания.
    c.a    ; 1 bute + 0 байтов добавка, так как следующее поле тоже не требует выравнивания.
    e.a    ; 1 bute + 6 байтов добавка, так как структура выравнивается до значения самого большого поля.
EndStructure

Debug SizeOf(Test1)  ; размер структуры  40 байтов
Debug SizeOf(Test2)    ; размер структуры  32 байта

Данный код показывает, как можно сэкономить память правильно организовав наполнение структуры. Здесь две одинаковые по содержанию структуры занимают разное количество памяти. Считается, что так можно сэкономить до 40% памяти занимаемой экземпляром структуры.

Дело в том, что по стандарту языка C, выравнивание происходит в соответствии со следующим правилом: каждое поле выравнивается по адресу, кратному размеру данного поля. Поле типа integer на 64-битной системе будет выровнено по границе 8 байт, long по границе 4 байта, word по границе 2 байта, и т.д. Поля типов ascii и byte не выравниваются. Размер структуры выравнивается до размера, кратного размеру его максимального элемента. Эта возможность только для опытных пользователей.

Функции для работы со структурами.

SizeOf – позволяет получить размер в байтах любой сложной Структуры, (а так же базовых типов поддерживаемых Purebasic.)

OffsetOf - позволяет получить смещение в памяти (в байтах) указанного поля, относительно стартового адреса Структуры.

TypeOf() – позволяет узнать тип любой объявленной переменной, экземпляра структуры или поля структуры.

With: EndWith - при использовании большого количества полей структуры, позволяют уменьшить объем вводимого кода и облегчить его читаемость.

AllocateMemory() – Позволяет выделить память для указанного экземпляра структуры.

InitializeStructure() – Инициализирует указанный экземпляр структуры, после того как для него выделена память с помощью AllocateMemory.

ClearStructure - освобождает область памяти занятую Структурой, и устанавливает значение всех полей на ноль.

FreeMemory() - Освобождает память, ранее выделенную для структуры с помощью AllocateMemory().

AllocateStructure() – Динамически создаёт в памяти новый экземпляр структуры, который сразу будет проинициализирован и готов к использованию.

FreeStructure() – Удаляет экземпляр структуры, созданный с помощью AllocateStructure().

CopyStructure() - Копирует содержимое области памяти занятой указанной структурой в область памяти занятой другой структурой. Это может применяться при работе с динамическим размещением структур, через Указатели.

Динамическое создание экземпляров структур.

Экземпляры структур можно создавать статически и динамически.
Статическое создание экземпляров структур – это значит создание во время компиляции, например, методом объявления переменной с типом структуры. Но если мы не знаем, сколько экземпляров структуры нам может понадобиться, этот способ малопригоден. Для таких случаев, можно применить динамическое создание экземпляров структур.
Динамическое создание - это значит: создание во время выполнения программы. Как пример, применение функции AllocateMemory() – это динамическое выделение памяти. Так же себя ведут динамические объекты вроде: Связных списков и Хеш-карт, которые организовывают новые элементы на ходу, по мере необходимости. Массив тоже можно отнести к динамическим объектам, потому как для объявления массива можно использовать выражения с переменными, а также изменять его размер по ходу программы.

Для динамического создания экземпляров структур можно использовать: Массив, Связный список и Хеш-карту

Structure Student ; Создаём шаблон структуры
    name.s
    age.a
    ball.f
EndStructure

Dim MyArray.Student(10)  ;  Объявляем массив структур Student, в виде массива MyArray() с типом Student.
NewList MyList.Student() ;  Объявляем Связный список структур Student, в виде Связного списка MyList() с типом Student.
NewMap MyMap.Student()    ;  Объявляем Хеш-карту (Ассоциативный массив) структур Student, в виде Хеш-карты MyMap() с типом Student.

В этом примере, каждый уже существующий и будущий элемент MyArray(), MyList(), MyMap() – является экземпляром структуры 'Student'. Здесь Purebasic берёт на себя функции по управлению памятью и инициализации экземпляров структуры. Но если требуется более гибкий подход, можно динамически создать экземпляр структуры с принудительным выделением под него памяти и последующей принудительной инициализацией. Для работы с таким экземпляром структуры необходимо использовать Указатель, который объявляется с типом данной структуры. Есть два способа получить такой экземпляр структуры.

Первый способ - для создания экземпляра использовать комбинацию функций AllocateMemory(SizeOf(Test)) + InitializeStructure(), а для удаления ClearStructure() + FreeMemory().

Второй способ - для создания экземпляра использовать функцию: AllocateStructure(), а для удаления: FreeStructure(). Этот вариант более автоматизирован.

AllocateStructure() совмещает в себе действие функций AllocateMemory(SizeOf(Test)) и InitializeStructure(), а FreeStructure() соответственно ClearStructure() и FreeMemory(). Но это не значит, что можно создать экземпляр структуры с помощью AllocateMemory() и InitializeStructure(), а освободить функцией FreeStructure(). Это приведет к ошибкам, т.к. функции несовместимы.

Пример: Динамическое создание структуры с помощью InitializeStructure().

Structure Test ; Создаём шаблон структуры Test
    s.s
    x.i
    List xx.i()
EndStructure

*Res.Test = AllocateMemory(SizeOf(Test)) ; Создаём экземпляр структуры Test
InitializeStructure(*Res.Test, Test)
*Res\s = Str(x)
*Res\x = x

For i=0 To 2
    If AddElement(*Res\xx())
        *Res\xx() = i*x
        Debug "Работает"
    EndIf
Next

ClearStructure(*Res, Test) ; Освобождаем память от структуры, сама структура остаётся.
FreeMemory(*Res)          ; Освобождаем память выделенную с помощью AllocateMemory.

Пример: Динамическое создание структуры с помощью AllocateStructure().

Structure Test ; Создаём шаблон структуры Test
    s.s
    x.i
    List xx.i()
EndStructure

*Res.Test = AllocateStructure(Test) ; Создаём экземпляр структуры Test
*Res\s = Str(x)
*Res\x = x

For i=0 To 2
    If AddElement(*Res\xx())
        Debug "Работает"
        *Res\xx() = i*x
    EndIf
Next
FreeStructure(*Res) ; Удаляем экземпляр структуры

Пример: Динамическое создание структур в процедуре.

Structure Test ; Создаём шаблон структуры Test
    s.s
    x.i
EndStructure

Procedure CreateObject(x)

    *Res.Test = AllocateStructure(Test) ; Создаём экземпляр структуры Test
    *Res\s = Str(x)
    *Res\x = x

    ProcedureReturn *Res
EndProcedure

*x1.Test = CreateObject(1)
*x2.Test = CreateObject(2)
*x3.Test = CreateObject(3)
*x4.Test = CreateObject(4)

Debug *x1\s
Debug *x1\x
Debug *x2\s
Debug *x2\x
Debug *x3\s
Debug *x3\x
Debug *x4\s
Debug *x4\x

FreeStructure(*x1)
FreeStructure(*x2)
FreeStructure(*x3)
FreeStructure(*x4)

Пример: Динамическое создание структур в цикле.

Structure Test ; Создаём шаблон структуры Test
    s.s
    x.i
EndStructure
NewList List() ; Связный список для хранения адресов экземпляров структур.

For x=1 To 10
    *Res.Test = AllocateStructure(Test) ; Создаём экземпляр структуры Test
    *Res\s = Str(x)
    *Res\x = x
    AddElement(List()) ; добавляем элемент для хранения нового адреса.
    List() = *Res      ; Запоминаем адрес очередного экземпляра структуры.
Next x

FirstElement(List())

For x =1 To 10 ; В цикле выводим содержимое экземпляров структур.
    *Res.Test = List()
    Debug *Res.Test\s
    Debug *Res.Test\x
    NextElement(List())
Next x

FirstElement(List())

For x= 1 To 10  ; В цикле удаляем все структуры по очереди.
    *Res.Test = List()
    FreeStructure(*Res)
    NextElement(List())
Next x




Структурное объединение



Введение

Структурное объединение – это ещё один определяемый пользователем сложный тип данных, представляющий собой объединение нескольких элементов структуры (возможно разных типов), при этом для каждого из объединяемых элементов выделяется одна и та же область памяти, т.е. они перекрываются.
Хотя доступ к этой области памяти возможен с использованием любого из объединённых элементов, элемент для этой цели должен выбираться так, чтобы полученный результат не был бессмысленным.
Доступ к элементам объединения осуществляется тем же способом, что и к структурам, то есть через символ "\" и имя элемента (поля). В отличие от C/C++ в Purebasic структурное объединение может создаваться только внутри обычной структуры.

Для создания объединения в Purebasic есть ключевые слова StructureUnion : EndStructureUnion. Элементы структуры находящиеся между этими операторами входят в одно объединение. В одной структуре может быть несколько объединений.


Синтаксис

Structure <name>
    StructureUnion
        Field1.Type
        Field2.Type
        ...
    EndStructureUnion
EndStructure


Описание


Объединение применяется для следующих целей:
- инициализации используемого объекта памяти, если в каждый момент времени только один объект из многих является активным, что позволяет экономить память.
- интерпретации основного представления объекта одного типа, как если бы этому объекту был присвоен другой тип. Это позволяет использовать объединение как преобразователь типов значения.

Память, которая соответствует объединению элементов, определяется величиной, необходимой для размещения наиболее длинного элемента. Если используется элемент меньшей длины, то объединение может содержать неиспользуемую память. Все элементы объединения хранятся в одной и той же области памяти, начиная с одного адреса. Для того чтобы объединить элементы числовых типов с типом 'string', последнему необходимо указать количество символов в фигурных скобках, например s.s{10}, другими словами, использовать не тип 'String', а тип 'Fixed String'.

Пример: Преобразователь типов.

Structure Type  ; Создаём шаблон структуры Type.
    StructureUnion ; Создаём объединение элементов внутри структуры Type.
        Long.l      ; Каждое поле (Long, Float, Byte, integer и string) расположено
        Float.f      ; по одному и тому же адресу в памяти.
        Byte.b
        integer.i
        string.s{4}  ; При объединении с другими типами, для string указывайте количество символов в фигурных скобках.
    EndStructureUnion
EndStructure
q.type\integer = 77 ; Присвоим 77 переменной с типом Integer.
Debug q.type\Float    ; 0.0
Debug q.type\Long    ; 77
Debug q.type\Byte    ; 77
Debug q.type\integer; 77
Debug q.type\string    ; Здесь отобразит заглавную М, что соответствует 77 в ASCII.
End

Пример: Применение преобразования типов для объединения и разделения многокомпонентных значений.

Structure RGBA  ;  Создаём шаблон Структуры RGBA (тип RGBA, назовите тип как Вам удобно),
    StructureUnion ;  и вкладываем в него Структурное Объединение,
        color.l      ;  в котором объединяем переменную 'color' с типом Long (4 байта),
        byte.a[4]  ;  и статический массив с типом ASCII (1 байт), элементы массива накладываются на байты переменной 'color'.
    EndStructureUnion
EndStructure

col.RGBA  ; Создаём экземпляр структуры RGBA, в виде переменной col с типом RGBA.

col\color = RGBA(255, 200, 150, 100)  ; Присваиваем полю 'color' значение RGBA.
Debug "Красный:  " + col\byte[0]      ; Считываем значение из нужного элемента массива (он же байт переменной 'color').
Debug "Зелёный:  " + col\byte[1]      ; Так можно объединять и разделять много-компонентные значения на составляющие.
Debug "Синий:    " + col\byte[2]      ; В данном случае получилось разделение, но если записать компонентные значения по
Debug "Прозрачность:  " + col\byte[3] ; элементам массива, то значение считанное из col\color - будет полным значением RGBA.
End

Пример: Более сложный пример (работа с датами).

Structure date
    day.s{2}
    pk1.s{1}
    month.s{2}
    pk2.s{1}
    year.s{4}
EndStructure

Structure date2
    StructureUnion
        s.s{10}
        d.date
    EndStructureUnion
EndStructure

Dim d1.date2(5)

d1(0)\s = "05.04.2008"
d1(1)\s = "07.05.2009"

Debug d1(0)\d\day
Debug d1(0)\d\month
Debug d1(0)\d\year

Debug d1(1)\d\day
Debug d1(1)\d\month
Debug d1(1)\d\year

d2.date2\s = "15.11.2010"

Debug d2\d\day
Debug d2\d\month
Debug d2\d\year
End