检查 std::string 的结构¶
当你剥离 std::string 类的成员函数和其他非数据组件时,剩下的结构是这样的:
class string {
union {
char* dataP;
char dataA[16];
};
int length;
// 指向内存中的字符串
string* _str = (string*)stringAddress;
这个类保留了 16 个字符,大概用于就地存储字符串。但它也声明前 4 个字节可以是指向字符的指针。这看起来奇怪,但它是优化的结果。在某个时刻,这个类的开发者决定 15 个字符(加上null 终止符)是许多字符串合适的长度,他们选择提前保留 16 字节内存来节省内存分配和释放。为了容纳更长的字符串,他们允许这块保留内存的前 4 个字节用作指向这些更长字符串字符的指针。
注 如果代码编译为 64 位,那么实际上会是前 8 个(而不是 4 个)字节指向字符。不过,在整个示例中,你可以假设 32 位地址,且 int 是地址的大小。
这样访问字符串数据会有一些开销。定位正确缓冲区的函数看起来像这样:
const char* c_str() {
if (_str->length <= 15)
return (const char*)&_str->dataA[0];
else
return (const char*)_str->dataP;
}
std::string 既可以是完整字符串,也可以是指向更长字符串的指针,这一事实使这个特定结构从游戏破解的角度看相当棘手。有些游戏可能用 std::string 存储很少超过 15 个字符的字符串。这种情况下,你可能会实现依赖这些字符串的机器人,却永远不知道底层结构实际上比简单字符串复杂得多。