pascal字符串如何在内存中表示?

pascal字符串如何在内存中排列?

我读到: http : //www.freepascal.org/docs-html/ref/refsu12.html它说字符串存储在堆上并且引用计数。 为了确定存储长度和引用的位置,我创建了一个字符串并对其进行了大量测试:

type PInt = ^Integer; var str: String; begin str := 'hello'; writeln(PInt(@str[1]) - (sizeof(integer) * 1)); //length writeln(PInt(@str[1]) - (sizeof(integer) * 2)); //reference count end. 

第一个打印长度,第二个打印参考计数。 它做得非常好并且有效。

现在我试图在C中模仿同样的事情:

 Export char* NewCString() { const char* hello_ptr = "hello"; int length = strlen(hello_ptr); //allocate space on the heap for: sizeof(refcount) + sizeof(int) + strlength char* pascal_string = (char*)malloc((sizeof(int) * 2) + length); *((int*)&pascal_string[0]) = 0; //reference count to 0. *((int*)&pascal_string[sizeof(int)]) = length; //length of the string. strcpy(&pascal_string[sizeof(int) * 2], hello_ptr); //copy hello to the pascal string. return &pascal_string[sizeof(int) * 2]; //return a pointer to the data. } Export void FreeCString(char* &ptr) { int data_offset = sizeof(int) * 2; free(ptr - data_offset); ptr = NULL; } 

然后在帕斯卡尔我做:

 var str: string; begin str := string(NewCString()); writeln(PInt(@str[1]) - (sizeof(integer) * 1)); //length - prints 5. correct. writeln(PInt(@str[1]) - (sizeof(integer) * 2)); //reference count - prints 1! correct. //FreeCString(str); //works fine if I call this.. end. 

pascal代码正确打印长度,并且由于赋值,引用计数增加1。 这是对的。

但是,一旦执行完毕,它就会严重崩溃! 它似乎试图释放字符串/堆。 如果我自己调用FreeCString,它就可以了! 我不确定发生了什么。

任何想法为什么崩溃?

仅仅因为运行时系统在内存中以特定方式布置字符串,并不意味着编写C代码来复制该内存布局将起作用。 字符串管理可能涉及其他约束或外部数据结构。 要使字符串与FreePascal兼容,请使用FreePascal自己的库例程。

这听起来像FreePascal需要除了free()之外的东西,当refcount归零时,但如果没有一些逆向工程或深入研究ABI规范,它很可能无法分辨。

  1. “string”是一个别名,可以指向3种不同的字符串类型(shortstring,ansistring和unicodestring)
  2. ansistring和unicodestring改变了从FPC 2.6到FPC 2.7.x +的布局(等于Delphi 2007到Delphi 2009)
  3. 任何Delphi内存分配器都必须能够分辨出已分配块的大小。 通常这是通过在块中放入32位大小来完成的。
  4. FreePascal和Delphi都有可插拔的内存分配器。 默认的Free Pascal管理器是一个自己的子分配器。 要使用(在* nix上)libc使用的任何内容,请使用unit cmem作为主程序中的第一个单元。
  5. 由于ansistring和unicodestring是使用手动技巧进行重新计算的,因此您负责维护引用计数的完整性。 其中包括为Pascal < - > C转换维护Pascal ABI。

总之,不要 ,并且必须在极少数情况下向pascal添加构造函数和析构函数,并通过它进行所有分配。

你可能想看一下Windows上的rtl / inc / astrings.inc Ps2,最容易使用COM兼容的宽带(BSTR)来处理中介语字符串类型。