VarSetCapacity()

AutoHotKey

VarSetCapacity()

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

GrantedCapacity := VarSetCapacity(UnquotedVarName [, RequestedCapacity, FillByte])

参数

UnquotedVarName

变量名 (无百分号包围的). 例如: VarSetCapacity(MyVar, 1000).
或者是动态变量, 例如 Array%i% .
还可以是函数的 ByRef 参数.

RequestedCapacity

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

指定一个数字则表示调节变量所能接受的容量大小,以字节为单位. 同时清空变量现有内容!
如果变量用于存储 Unicode 字符串, 本参数应设为 ANSI 字符串的两倍(即 x2).

译者注: ANSI字符串一个英文字母占用一字节,而中文字每个占用两个字节.

本参数无需考虑内部零终止符.
例如, 设为 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() 可返回以字节为单位的实际内容的长度而不是变量的容量.

FillByte

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

返回值

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

备注

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

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

相关

DllCall, #MaxMem, NumPut, NumGet

示例

; 通过赋予 MyVar 充足的内存空间来优化整体性能.
VarSetCapacity(MyVar, 10240000)  ; ~10 MB
Loop
{
    ...
    MyVar = %MyVar%%StringToConcatenate%
    ...
}
; 计算字符串需要的缓冲空间.
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%
    )
}