03-21-2026, 11:52 PM
Pawn 中的压缩字符串
原文作者: Emmet_ 本篇教程仅为翻译搬运
引言
压缩字符串自 SA-MP 和 Pawn 诞生之初就已存在。然而,很多人并不了解压缩字符串以及它能节省多少内存!本教程将教你压缩字符串的基础知识,以及如何正确操作它们。
什么是压缩字符串?
压缩字符串是一种数组,它将数据存储在每个字节中,而不是像普通数组那样存储在每个单元格中。压缩字符串以小端序存储(即低位字节在前),并且只能容纳 0 到 255 的 ASCII 字符,超出这个范围的数值会绕回。
看这段代码:
代码:
new string[5];
string[0] = 'a';
string[1] = 'b';
string[2] = 'c';
string[3] = 'd';
string[4] = '\0';'a' 存储在单元格 0,'b' 存储在单元格 1,依此类推。一个单元格基本上占用 4 个字节,所以算一下,上面的字符串大约占用 20 个字节,每个字符占用 4 个字节的空间。然而,使用下面的代码:
代码:
new string[5 char];
string{0} = 'a';
string{1} = 'b';
string{2} = 'c';
string{3} = 'd';
string{4} = '\0';'a' 存储在字节 0,'b' 存储在字节 1,依此类推。实际上,上面的字符串只占用 8 个字节,仅包含 2 个单元格!你可能在想为什么这个字符串不是 5 个字节。使用
char 修饰符会自动将数组大小向上取整到最近的 4 的倍数(例如,1 变成 4,3 变成 4,5 变成 8,23 变成 24,依此类推)。代码:
// 这个数组占用 5 个单元格,共 20 个字节。
new string[5] = "abcd";
// 这个数组占用 2 个单元格,共 8 个字节。
new string[5 char] = !"abcd";因此,使用压缩字符串可以节省 3 到 4 倍的内存!
适用场景
你可能认为压缩数组没什么用。如果这么想,那你就错了。压缩数组有很多用途,不仅仅是节省内存!
稀疏数组
首先,你可以阅读我关于稀疏数组的教程:
https://sampforum.blast.hk/showthread.php?tid=480439
稀疏数组就是那些大部分数据经常为空的数组。使用压缩数组可以轻松节省内存!
代码:
#define MAX_ITEMS (64)
// 8,192 个单元格 = 32,768 字节!
new gData[MAX_ITEMS][128];
// 2,048 个字节 = 8,192 个单元格!
new gData[MAX_ITEMS][128 char];不常使用的字符串
很多时候,你会把字符串保存到内存中,但很少使用它们(例如几乎不用)。对于这类数组,压缩数组非常适用。
大量数据存储
回到上面的“稀疏数组”部分,如果你需要存储大量数据,最好使用压缩字符串。
内存节省!
压缩数组能节省 4 倍的内存,既然可以用压缩数组,为什么还要用那些多占 4 倍内存的方式存储数据呢?
SA-MP 中的不支持情况
压缩字符串在纯 Pawn 中完美支持。但是,大多数 SA-MP 原生函数不支持压缩字符串,比如
format、GetPlayerName 等。如果你打算使用压缩字符串,就必须依赖
strpack、strunpack 以及 string.inc 中的其他字符串函数。格式化字符串
format 和 printf 函数不支持压缩字符串,因此你必须使用 strpack:代码:
new
string[128 char];
strpack(string, "Hello world!");你也可以这样做:
代码:
new
string[128 char],
temp[128]
;
strpack(string, "Emmet");
strunpack(temp, string);
format(temp, sizeof(temp), "%s likes to eat %s.", temp, "Big Macs");
strpack(string, temp);支持压缩字符串的函数
string.inc 中的所有字符串函数都同时支持压缩数组和非压缩数组。fread 和 valstr 函数也接受一个可选的 pack 参数来支持压缩数组,因此你不用担心它们是否能与压缩数组一起工作。访问数据
在 Pawn 中,非压缩数组将数据存储在每个单元格中。而压缩数组将数据存储在每个字节中,这意味着你不能像访问非压缩数组那样使用方括号
[] 来访问和获取压缩数组中的数据。代码:
// 错误。
if (g_PackedString[0] != '\0')
{
g_PackedString[0] = 'h';
g_PackedString[1] = 'i';
}
// 正确!
if (g_PackedString{0} != '\0')
{
g_PackedString{0} = 'h';
g_PackedString{1} = 'i';
}另外,要设置一个压缩字符串,你需要在字符串前加上感叹号,以表示这是压缩输入。
代码:
// 错误。这会尝试将字符串按单元格存储,这不是我们想要的!
g_PackedString = "Hello world.";
// 正确!
g_PackedString = !"Hello world.";
