Skip to content

FreeBASIC 动态内存管理


使用 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 个对象:CallocateReallocateDeallocate(+ .constructor + .destructor)。

  • 3 个对象:NewDelete

  • 3 个对象:Placement New(+ .destructor)。

  • 3 个然后 4 个对象:RedimErase

start GeSHi

vb
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

Sleep

end 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 销毁并释放图像的存储空间。

另请参阅

返回 目录

基于 FreeBASIC 官方文档翻译 如有侵权请联系我们删除
FreeBASIC 是开源项目,与微软公司无隶属关系