ZetCode

FreeBasic Union 关键字

最后修改日期:2025 年 6 月 16 日

FreeBasic 的 Union 关键字定义了一种特殊的数据类型,它允许不同的变量共享同一内存空间。Union 对于内存高效的数据表示和类型等同非常有用。

基本定义

FreeBasic 中的 Union 是一种用户自定义数据类型,其中所有成员共享相同的内存地址。Union 的大小等于其最大的成员。

与结构体(structure)不同,结构体的每个成员都有自己的内存空间,而 Union 的成员在内存中是重叠的。写入一个成员会影响所有其他成员,因为它们共享相同的存储空间。

简单的 Union 示例

此示例演示了一个包含整数和浮点数成员的基本 Union。

simple_union.bas
Union Number
    As Integer i
    As Single f
End Union

Dim num As Number
num.i = 65

Print "Integer: "; num.i
Print "Float: "; num.f

在这里,我们创建了一个可以存储整数或浮点数的 Union。当我们设置整数值时,浮点数解释会显示相同的内存位,但被解释为浮点数。

包含不同数据类型的 Union

FreeBASIC 中的 Union 允许多个不同类型变量占用相同的内存地址。当您想在同一空间中存储不同种类的数据,但一次只存储一种时,这非常有用。这是一种内存高效的方式来表示重叠的数据格式或重新解释原始字节。

mixed_union.bas
Union Data
    As Byte b
    As Integer i
    As Double d
End Union

Dim ud As Data
ud.d = 3.14159

Print "Byte: "; ud.b
Print "Integer: "; ud.i
Print "Double: "; ud.d

在此示例中,Data Union 包含三个成员:一个 Byte,一个 Integer,以及一个 Double。它们都共享相同的内存空间。当我们为 ud.d 赋值时,内存将被填充为双精度浮点数 3.14159 的二进制表示。然后,从 ud.bud.i 读取会分别将相同的字节解释为 ByteInteger

这种技术常用于底层编程,用于类型等同、二进制序列化或解释硬件寄存器等任务。但是,应谨慎使用,因为从最近未写入的 Union 成员读取可能会产生意外的结果。

Union 的大小由其最大的成员决定—在此例中为 Double,通常占用 8 个字节。所有其他成员都覆盖在同一内存块之上。

用于类型等同的 Union

Union 常用于类型等同—将数据作为不同类型访问。

type_punning.bas
Union FloatToBytes
    As Single f
    As Byte bytes(0 To 3)
End Union

Dim ftb As FloatToBytes
ftb.f = 123.456

For i As Integer = 0 To 3
    Print "Byte "; i; ": "; Hex(ftb.bytes(i))
Next

此示例展示了如何检查浮点数的各个字节。Union 允许我们将相同的内存作为浮点数或字节数组访问,从而揭示浮点数的内部表示。

结构体中的 Union

Union 可以嵌入到结构体中,以实现更复杂的数据布局。

union_in_struct.bas
Type Variant
    typeId As Integer
    Union
        i As Integer
        f As Single
        s As String * 10
    End Union
End Type

Dim v As Variant
v.typeId = 1  ' Integer type
v.i = 42

Print "Type: "; v.typeId
Print "Value: "; v.i

这演示了一个包含 Union 的结构体。Variant 类型可以存储整数、浮点数或字符串。typeId 字段指示当前哪个成员是有效的。这种模式在需要灵活处理多种数据类型的 Datalogic 结构中很常见。

用于硬件访问的 Union

Union 对于访问硬件寄存器或打包数据格式很有用。

hardware_union.bas
Type StatusRegister
    raw As UShort
End Type

Function IsReady(sr As StatusRegister) As UShort
    Return (sr.raw And &b0000000000000001)
End Function

Function HasError(sr As StatusRegister) As UShort
    Return (sr.raw And &b0000000000000010) Shr 1
End Function

Function IsBusy(sr As StatusRegister) As UShort
    Return (sr.raw And &b0000000000000100) Shr 2
End Function

Dim sr As StatusRegister
sr.raw = &h8003  ' Binary: 1000 0000 0000 0011

Print "Raw: "; Hex(sr.raw)
Print "Ready: "; IsReady(sr)
Print "Error: "; HasError(sr)
Print "Busy: "; IsBusy(sr)

此示例模拟了一个硬件状态寄存器。Union 允许我们访问原始值,同时提供解释特定位作为标志的函数。这在直接访问硬件寄存器的底层编程中很常见。

用于网络协议的 Union

Union 可以表示具有不同解释的协议数据单元。

network_union.bas
Type OctetArray
    o As UByte
    o1 As UByte
    o2 As UByte
    o3 As UByte
End Type

Union IPAddress
    addr As UInteger
    octets As OctetArray
End Union

Dim ip As IPAddress
ip.addr = &hC0A80101  ' 192.168.1.1

Print "Dotted: "; ip.octets.o3; "."; ip.octets.o2; "."; ip.octets.o1; "."; ip.octets.o
Print "Hex: "; Hex(ip.addr)

按照 o3.o2.o1.o 的顺序打印字节的原因在于计算机如何将多字节值存储在内存中,这是一种称为字节序(endianness)的概念。大多数现代 PC 使用小端格式(little-endian),其中最低有效字节 (o) 存储在最低内存地址,最高有效字节 (o3) 存储在最高内存地址。当 IP 地址存储为 32 位整数时,其字节在内存中从低到高排列。

然而,IP 地址的标准点分十进制表示法(例如 192.168.1.1)要求先出现最高有效字节。因此,为了正确显示地址,我们先打印 o3(最高字节),最后打印 o(最低字节)。这种方法确保输出与人类可读的网络标准格式匹配。需要注意的是,网络协议通常使用大端字节序(网络字节序),因此在不同系统之间传输或接收二进制数据时,您可能需要转换主机字节序和网络字节序,以确保数据的正确解释。

包含数组的 Union

Union 可以包含数组,以实现更灵活的数据访问模式。

array_union.bas
Union Matrix
    As Single values(0 To 15)
    As Single m(0 To 3, 0 To 3)
End Union

Dim mat As Matrix

' Fill the matrix
For i As Integer = 0 To 15
    mat.values(i) = i
Next

' Access as 4x4 matrix
For y As Integer = 0 To 3
    For x As Integer = 0 To 3
        Print mat.m(y, x); " ";
    Next
    Print
Next

此示例显示了一个线性数组和 4x4 矩阵之间的 Union。两种视图访问相同的内存,允许根据算法的需求将数据视为扁平数组或二维矩阵。

本教程介绍了 FreeBasic 的 Union 关键字,并通过实际示例展示了它在不同场景下的用法。

作者

我叫 Jan Bodnar,是一名充满激情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在教授编程方面拥有十多年的经验。

列出所有 FreeBasic 教程