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

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

 TString *str = (TString*)malloc(sizeof(TString) + ); 

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

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

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

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

但C99增加了灵活的arrays成员,因此可以按照标准兼容的方式完成

 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) 

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

当在C中编写相同类型的function时,由于不存在类和虚拟inheritance,因此更加困难。

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

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

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

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