VarSetCapacity()

扩展变量的容量或释放其内存. 通常只有在特殊情况下才有必要调用本函数, 如 DllCall().

GrantedCapacity := VarSetCapacity(TargetVar , RequestedCapacity, FillByte)

参数

TargetVar

变量的引用. 例如: VarSetCapacity(MyVar, 1000). 这也可以是动态变量, 例如 Array%i% 或是函数的 ByRef 参数.

RequestedCapacity

如果省略, 则函数将返回变量当前的容量且不会改变其的内容. 否则, 变量中的所有内容将会丢弃(变量被置空).

RequestedCapacity 指定调整后变量所能容纳的字节数. 对于 Unicode 字符串, 本参数应设为 ANSI 字符串的两倍(即 x2). 译者注: ANSI 字符串一个英文字母占用一字节, 而中文字每个占用两个字节. RequestedCapacity 无需考虑内部零终止符. 例如, 指定 1 表示修改变量容量为最多可接受一字节字符串的内容加上内部终止符. 注意: AHK 脚本中给变量赋值是自动扩展变量容量大小的, 即使之前用本函数将变量设为一个较小的容量, 也不会影响之后给变量传递更大的值.

因此可以经常简单地调用此函数来确保变量占用最小的空间, 考虑到性能原因, 只有在 RequestedCapacity 为 0 时, 才会缩小变量的容量. 换句话说, 如果变量的容量已经大于 RequestedCapacity, 不会减小它的容量(但为了保持一致, 变量会被置空).

因此, 要明确地缩小变量, 首先请使用 VarSetCapacity(Var, 0) 释放它占用的内存, 然后使用 VarSetCapacity(Var, NewCapacity) 或者简单地让它自动按需从零扩展.

考虑到性能的原因, 要释放一个原来容量小于 64 字节(在 Unicode 构建中为 128 字节) 的变量可能会没有效果, 因为它的内存是永久类型的. 此时, 会返回当前容量而不是 0.

考虑到性能的原因, 容量小于 4096 字节的变量的内存不能通过赋值空字符串的方法来释放(例如 Var := ""). 然而, VarSetCapacity(Var, 0) 可释放它.

[v1.0.44.03+]: 可通过将 RequestedCapacity 设为 -1, 以将变量的内部存储的字符串长度更新为其当前内容的长度. 这个特性在变量被间接修改时特别有用, 例如将变量的地址递给 DllCall(). 在这种模式中, VarSetCapacity() 可返回以字节为单位的实际内容的长度而不是变量的容量. 译者注: 某些 Dll 中的函数返回的变量, 其长度为 0, 直接调用时该变量表现为空值, 需要使用 VarSetCapacity(Var, -1) 将变量长度更新为其当前内容的实际长度.

FillByte

此参数通常省略, 此时不会对目标变量的内存进行初始化(作为替代, 只是像上面描述的那样简单地把变量置空). 否则, 请指定介于 0 和 255 之间的整数. 目标变量内存区域的每个字节(它的当前容量, 可能大于 RequestedCapacity) 会被设为这个数字. 零显然是最常用的值, 这可用于要在变量中保存原始的二进制数据时, 例如 DllCall 结构.

返回值

该函数返回 TargetVar 当前可容纳的字节数, 一般来说大于或等于 RequestedCapacity. 如果 TargetVar 不是一个有效的变量名称(比如直接传递字符串值或数字), 则返回数字 0 . 如果发生极其罕见的系统内存不足的情况, 将弹出一个错误提示对话框同时返回当前的容量大小. (以后的版本可能会修改这样的行为).

备注

本函数除了经常用于配合 DllCall() 之外, 还可用于逐步构建较大字符串时提升性能. 比如当您能够确定字符串的最终长度时可以避免多次自动调整容量. 在这种情况中, RequestedCapacity 不需要太准确: 如果容量太小, 性能仍然会提升, 而当分配的容量用完时变量会开始自动扩展. 如果容量太大, 会浪费一些内存, 不过只是暂时的, 因为在操作完成后使用 VarSetCapacity(Var, 0)Var := "" 可释放变量占用的内存.

#MaxMem 仅用于限制变量自动扩容, 而不会影响 VarSetCapacity().

DllCall(), #MaxMem, NumPut(), NumGet()

示例

通过赋予 MyVar 充足的内存空间来优化整体性能.

VarSetCapacity(MyVar, 10240000)  ; ~10 MB
Loop
{
    ; ...
    MyVar .= StringToConcatenate
    ; ...
}

使用一个变量通过 DllCall() 从外部函数来接收一个字符串.

; 计算字符串需要的缓冲空间.
bytes_per_char := A_IsUnicode ? 2 : 1
max_chars := 500
max_bytes := max_chars * bytes_per_char

Loop 2
{
    ; 分配用于 DllCall 的空间.
    VarSetCapacity(buf, max_bytes)

    if (A_Index = 1)
        ; 通过 DllCall 间接修改变量.
        DllCall("wsprintf", "Ptr", &buf, "Str", "0x%08x", "UInt", 4919)
    else
        ; 使用 "str" 来自动更新长度:
        DllCall("wsprintf", "Str", buf, "Str", "0x%08x", "UInt", 4919)

    ; 连接字符串以演示为什么需要更新长度:
    wrong_str := buf . "<end>"
    wrong_len := StrLen(buf)

    ; 更新变量的长度.
    VarSetCapacity(buf, -1)

    right_str := buf . "<end>"
    right_len := StrLen(buf)

    MsgBox,
    (
    Before updating
      String: %wrong_str%
      Length: %wrong_len%

    After updating
      String: %right_str%
      Length: %right_len%
    )
}