FreeBASIC 动态内存管理
- 来源: https://www.freebasic.net/wiki/wikka.php?wakka=ProPgDynamicMemory
- 最后更新: 2022-07-10
使用 FreeBASIC 管理动态内存(分配/释放)
前言:
FreeBASIC 支持三种基本的内存分配类型:
静态分配用于静态变量和全局变量:内存在程序运行时分配一次,并在程序的整个生命周期内持续存在。
栈分配用于过程参数和局部变量:内存在进入相应代码块时分配,在离开代码块时释放,可重复多次。
动态分配是本页面的主题。
静态分配和栈分配有两个共同点:
变量的大小必须在编译时已知。
内存的分配和释放是自动发生的(当变量被实例化然后销毁时)。用户无法提前销毁此类变量。
大多数情况下,这没问题。但是,在某些情况下,这些约束中的一个或另一个会导致问题(当所需内存取决于用户输入时,大小只能在运行时确定)。
如果所有内容的大小必须在编译时声明,最好的方法是尝试估计所需变量的最大大小,并希望这足够。
这是一个糟糕的解决方案,原因至少有三个:
首先,如果内存未被完全使用,会导致浪费。
其次,大多数普通变量分配在称为栈的内存区域。程序的栈内存量通常很低(默认为 1 MB 或 2 MB)。如果超过此数量,将发生栈溢出,程序将中止。
第三,也是最重要的,这可能导致人为限制和/或溢出。如果所需内存超过预留内存会发生什么(停止程序、向用户发出消息,等等)。
幸运的是,这些问题通过动态内存分配轻松解决。动态内存分配是程序在需要时向操作系统请求内存的一种方式。这种内存不来自程序有限的栈内存,而是从操作系统管理的称为堆的更大内存池中分配。在现代机器上,堆可以有数 GB 的大小。
动态内存分配的关键字
有两组用于动态分配/释放的关键字:
Allocate/Callocate/Reallocate/Deallocate:用于原始内存分配和释放,适用于简单的预定义类型或缓冲区(如数字预定义类型、用户缓冲区等)。New/Delete:用于内存分配+构造,以及析构+释放,适用于对象类型(如可变长度字符串、UDT 等)。
在管理同一内存块时,强烈不建议混合使用这两组关键字。
使用 Allocate / Callocate / Reallocate / Deallocate 管理动态内存
对于每个关键字,请参阅各自文档页面中的详细语法、精确描述和示例。
额外功能和使用提示:
Allocate/Callocate/Reallocate允许知道内存分配是否成功(否则返回空指针)。即使请求分配 0 字节的内存,也会返回非空指针,其值应用于随后释放分配(
Reallocate(pointer, 0)除外,其行为类似于Deallocate)。对于内存释放,
Deallocate可以在任何类型的指针上调用(无论如何都要有正确的值)。如果用户绝对想将此类型的分配用于对象(例如为了能够进行重新分配),则需要用户在适当时候以正确方式手动调用构造函数和析构函数(使用成员访问运算符)。
释放后,指针值变为无效(指向无效内存地址),再次使用(用于解引用或再次调用
Deallocate)将导致未定义行为。
使用 New / Delete 管理动态内存
对于每个关键字,请参阅各自文档页面中的详细语法、精确描述和示例。
额外功能和使用提示:
之前,
New不会在内存分配失败时发出信号(程序挂起)。从 fbc 修订版 1.06 起,通过在New失败时返回空指针解决了此问题。即使请求分配 0 字节的内存(
New predefined_datatype[0]),也会返回非空指针,其值应用于随后释放分配。对于对象析构和内存释放,
Delete必须在正确类型的指针上调用(否则对象析构函数将不会被调用或被错误调用,这可能导致内存泄漏甚至Delete[]崩溃)。用户也可以将此类型的分配用于简单的预定义类型(固定长度字符串除外),但这在功能上不会增加任何内容,除了分配语法更简单。
析构+释放后,指针值变为无效(指向无效内存地址),再次使用(用于解引用或再次调用
Delete)将导致未定义行为。如果使用了特殊的
Placement New(使用已分配的内存),则只会进行对象构造,因此禁止使用Delete(以避免双重释放请求)。如果有必要,对象的唯一析构必须由用户手动完成(使用成员访问运算符调用析构函数)。
使用 Redim / Erase 的变体
FreeBASIC 也支持动态数组(可变长度数组)。
动态数组用于存储其元素的内存在运行时在堆中分配。动态数组可以包含简单类型以及复杂对象。
通过使用 Redim,用户不需要调用构造函数/析构函数,因为 Redim 在添加/删除元素时会自动执行此操作。Erase 然后销毁所有剩余元素,以完全释放分配给它们的内存。
比较 4 种方法的使用案例
在一组对象上的使用示例,比较 4 种方法:
3 个然后 4 个对象:
Callocate、Reallocate、Deallocate(+ .constructor + .destructor)。3 个对象:
New、Delete。3 个对象:
Placement New(+ .destructor)。3 个然后 4 个对象:
Redim、Erase。
start GeSHi
Type UDT
Dim As String S = "FreeBASIC" '' induce an implicit constructor and destructor
End Type
' 3 then 4 objects: Callocate, Reallocate, Deallocate, (+ .constructor + .destructor)
Dim As UDT Ptr p1 = CAllocate(3, SizeOf(UDT)) '' allocate cleared memory for 3 elements (string descriptors cleared,
'' but maybe useless because of the constructor's call right behind)
For I As Integer = 0 To 2
p1[I].Constructor() '' call the constructor on each element
Next I
For I As Integer = 0 To 2
p1[I].S &= Str(I) '' add the element number to the string of each element
Next I
For I As Integer = 0 To 2
Print "'" & p1[I].S & "'", '' print each element string
Next I
Print
p1 = Reallocate(p1, 4 * SizeOf(UDT)) '' reallocate memory for one additional element
Clear p1[3], 0, 3 * SizeOf(Integer) '' clear the descriptor of the additional element,
'' but maybe useless because of the constructor's call right behind
p1[3].Constructor() '' call the constructor on the additional element
p1[3].S &= Str(3) '' add the element number to the string of the additional element
For I As Integer = 0 To 3
Print "'" & p1[I].S & "'", '' print each element string
Next I
Print
For I As Integer = 0 To 3
p1[I].Destructor() '' call the destructor on each element
Next I
Deallocate(p1) '' deallocate the memory
Print
' 3 objects: New, Delete
Dim As UDT Ptr p2 = New UDT[3] '' allocate memory and construct 3 elements
For I As Integer = 0 To 2
p2[I].S &= Str(I) '' add the element number to the string of each element
Next I
For I As Integer = 0 To 2
Print "'" & p2[I].S & "'", '' print each element string
Next I
Print
Delete [] p2 '' destroy the 3 element and deallocate the memory
Print
' 3 objects: Placement New, (+ .destructor)
ReDim As Byte array(0 To 3 * SizeOf(UDT) - 1) '' allocate buffer for 3 elements
Dim As Any Ptr p = @array(0)
Dim As UDT Ptr p3 = New(p) UDT[3] '' only construct the 3 elements in the buffer (placement New)
For I As Integer = 0 To 2
p3[I].S &= Str(I) '' add the element number to the string of each element
Next I
For I As Integer = 0 To 2
Print "'" & p3[I].S & "'", '' print each element string
Next I
Print
For I As Integer = 0 To 2
p3[I].Destructor() '' call the destructor on each element
Next I
Erase array '' deallocate the buffer
Print
' 3 then 4 objects: Redim, Erase
ReDim As UDT p4(0 To 2) '' define a dynamic array of 3 elements
For I As Integer = 0 To 2
p4(I).S &= Str(I) '' add the element number to the string of each element
Next I
For I As Integer = 0 To 2
Print "'" & p4(I).S & "'", '' print each element string
Next I
Print
ReDim Preserve p4(0 To 3) '' resize the dynamic array for one additional element
p4(3).S &= Str(3) '' add the element number to the string of the additional element
For I As Integer = 0 To 3
Print "'" & p4(I).S & "'", '' print each element string
Next I
Print
Erase p4 '' erase the dynamic array
Print
Sleepend GeSHi
输出:
'FreeBASIC0' 'FreeBASIC1' 'FreeBASIC2'
'FreeBASIC0' 'FreeBASIC1' 'FreeBASIC2' 'FreeBASIC3'
'FreeBASIC0' 'FreeBASIC1' 'FreeBASIC2'
'FreeBASIC0' 'FreeBASIC1' 'FreeBASIC2'
'FreeBASIC0' 'FreeBASIC1' 'FreeBASIC2'
'FreeBASIC0' 'FreeBASIC1' 'FreeBASIC2' 'FreeBASIC3'使用 Imagecreate / Imagedestroy 的特定动态图像缓冲区分配
ImageCreate 分配并初始化图像的存储空间。
生成的图像缓冲区可以在任何屏幕模式下的绘图过程中使用,并且可以跨模式变换使用,只要图像缓冲区的颜色深度与图形屏幕的颜色深度匹配。
Imagedestroy 销毁并释放图像的存储空间。
另请参阅
返回 目录