为什么内部Lua字符串以他们的方式存储?

时间:2012-01-23 23:13:18

标签: c string struct 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类中被钻进m中以制作明确定义的结构。我知道我可能会在这里搅拌一个巢,但是你真的失去了那么多的速度/空间来定义一个结构作为数据头而不是定义该数据的指针值吗?

3 个答案:

答案 0 :(得分:5)

这个想法很可能是你在一大块数据而不是两个数据中分配标题和数据:

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

除了只调用malloc / free之外,还可以减少内存碎片并增加内存本地化。

但回答你的问题,是的,这些黑客通常是一种不好的做法,应该非常小心。如果你这样做,你可能希望将它们隐藏在一层宏/内联函数下。

答案 1 :(得分:2)

正如罗德里戈所说,我们的想法是将标题和字符串数据分配为单个内存块。值得指出的是,你也看到了非标准的黑客攻击

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)

答案 2 :(得分:1)

它涉及由更有限的C语言引起的并发症。在C ++中,您只需定义一个名为GCObject的基类,其中包含垃圾收集变量,然后TString将是一个子类,并使用虚拟析构函数,TString和它&# 39;随附的const char *块将被正确释放。

在C语言中编写相同类型的功能时,由于不存在类和虚拟继承,因此它有点困难。

Lua正在做的是通过插入管理其后面的内存部分的垃圾收集状态所需的头来实现垃圾收集。请记住,free(void *)不需要知道除内存块地址之外的任何内容。

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

Lua保留了这些&#34;可收集的链接列表&#34;内存块,在这种情况下是一个字符数组,这样它就可以有效地释放内存而不需要知道它所指向的对象的类型。

如果你的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;
}