为什么内部 Lua 字符串存储的方式是这样的?

我想要一个简单的字符串表,可以存储一堆常量,我想“嘿!Lua可以做到,让我使用一些它们的函数!”

这主要是在lstring.h/lstring.c文件中(我正在使用5.2)

我首先会展示我感兴趣的代码。它来自lobject.h

/*
** Header for string value; string bytes follow the end of this structure
*/
typedef union TString {
  L_Umaxalign dummy;  /* ensures maximum alignment for strings */
  struct {
    CommonHeader;
    lu_byte reserved;
    unsigned int hash;
    size_t len;  /* number of characters in string */
  } tsv;
} TString;

/* get the actual string (array of bytes) from a TString */
#define getstr(ts)  cast(const char *, (ts) + 1)

/* get the actual string (array of bytes) from a Lua value */
#define svalue(o)       getstr(rawtsvalue(o))

正如你所看到的,数据存储在结构外部。要获取字节流,需要获取TString的大小,加1,然后获得char*指针。

但这不是糟糕的编码吗?在我的C类中,已经强调了明确定义的结构。我知道我可能会触碰到敏感话题,但将一个结构定义为数据的标头而不是定义一个指针值来容纳该数据,你真的会失去那么多速度/空间吗?

原文链接 https://stackoverflow.com/questions/8979999

点赞
stackoverflow用户865874
stackoverflow用户865874

这里的想法可能是:您可以将标题和数据分配在一个大块数据中,而不是两个块中:

TString *str = (TString*)malloc(sizeof(TString) + <length_of_string>);

除了只调用一次malloc/free之外,还可以减少内存碎片化并增加内存本地化。但是,回答您的问题,是的,这种hack通常是不好的习惯,应该非常小心地处理。如果您这样做,您可能会希望通过宏/内联函数隐藏它们。

2012-01-23 23:21:50
stackoverflow用户166955
stackoverflow用户166955

正如 Rodrigo 所说,思路是将标题和字符串数据作为单个内存块进行分配。值得指出的是,您还可以看到非标准的 hack

struct lenstring {
  unsigned length;
  char data[0];
};

但是 C99 中添加了灵活的数组成员,所以可以以符合标准的方式完成,如下所示

struct lenstring {
  unsigned length;
  char data[];
};

如果 Lua 的字符串是这样做的,它会是这样的

typedef union TString {
  L_Umaxalign dummy;
  struct {
    CommonHeader;
    lu_byte reserved;
    unsigned int hash;
    size_t len;
    const char data[];
  } tsv;
} TString;

#define getstr(ts) (ts->tsv->data)
2012-01-23 23:57:55
stackoverflow用户151429
stackoverflow用户151429

这与C语言的限制有关。在C++中,你只需要定义一个名为 GCObject 的基类,其中包含垃圾回收变量,然后 TString 就是一个子类,并使用虚析构函数,即可正确释放 TString 和其附带的 const char * 块。

C语言中编写相同功能的代码要困难一些,因为类和虚继承在C中不存在。

Lua实现垃圾回收的方法是插入所需的头来管理其后面的内存块的垃圾回收状态。请记住,free(void*) 不需要知道除内存块地址外的任何信息。

#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked

Lua将这些 "collectable" 内存块的链表保留在内存中,以便可以有效地释放内存,而无需知道指向的对象类型。

如果 TString 指向另一个内存块,该内存块包含字符数组,则需要垃圾收集器确定对象的类型,然后深入其结构以 _同时_释放字符串缓冲区。

这种垃圾回收的伪代码如下:

GCHeader *next, *prev;
GCHeader *current = firstObject;

while(current)
{
    next = current->next;
    if (/* current is ready for deletion */)
    {
        free(current);

        // relink previous to the next (singly-linked list)
        if (prev)
            prev->next = next;
    }
    else
        prev = current; // store previous undeleted object
    current = next;
}
2014-12-21 05:22:24